package mindustry.graphics; import arc.*; import arc.graphics.*; import arc.graphics.Texture.*; import arc.graphics.g2d.*; import arc.graphics.gl.*; import arc.math.geom.*; import arc.struct.*; import arc.util.*; import mindustry.game.EventType.*; import mindustry.game.*; import mindustry.gen.*; import mindustry.world.*; import mindustry.world.meta.*; import static mindustry.Vars.*; public final class FogRenderer{ private FrameBuffer staticFog = new FrameBuffer(), dynamicFog = new FrameBuffer(); private LongSeq events = new LongSeq(); private Rect rect = new Rect(); private @Nullable Team lastTeam; public FogRenderer(){ Events.on(WorldLoadEvent.class, event -> { lastTeam = null; events.clear(); }); } public void handleEvent(long event){ events.add(event); } public Texture getStaticTexture(){ return staticFog.getTexture(); } public Texture getDynamicTexture(){ return dynamicFog.getTexture(); } public void drawFog(){ //there is no fog. if(fogControl.getDiscovered(player.team()) == null) return; //resize if world size changes boolean clearStatic = staticFog.resizeCheck(world.width(), world.height()); dynamicFog.resize(world.width(), world.height()); if(state.rules.staticFog && player.team() != lastTeam){ copyFromCpu(); lastTeam = player.team(); clearStatic = false; } //draw dynamic fog every frame { Draw.proj(0, 0, staticFog.getWidth() * tilesize, staticFog.getHeight() * tilesize); dynamicFog.begin(Color.black); ScissorStack.push(rect.set(1, 1, staticFog.getWidth() - 2, staticFog.getHeight() - 2)); Team team = player.team(); for(var build : indexer.getFlagged(team, BlockFlag.hasFogRadius)){ poly(build.x, build.y, build.fogRadius() * tilesize); } for(var unit : team.data().units){ poly(unit.x, unit.y, unit.type.fogRadius * tilesize); } dynamicFog.end(); ScissorStack.pop(); Draw.proj(Core.camera); } //grab static events if(state.rules.staticFog && (clearStatic || events.size > 0)){ //set projection to whole map Draw.proj(0, 0, staticFog.getWidth(), staticFog.getHeight()); //if the buffer resized, it contains garbage now, clearStatic it. if(clearStatic){ staticFog.begin(Color.black); }else{ staticFog.begin(); } ScissorStack.push(rect.set(1, 1, staticFog.getWidth() - 2, staticFog.getHeight() - 2)); Draw.color(Color.white); //process new static fog events for(int i = 0; i < events.size; i++){ renderEvent(events.items[i]); } events.clear(); staticFog.end(); ScissorStack.pop(); Draw.proj(Core.camera); } if(state.rules.staticFog){ staticFog.getTexture().setFilter(TextureFilter.linear); } dynamicFog.getTexture().setFilter(TextureFilter.linear); Draw.shader(Shaders.fog); Draw.color(state.rules.dynamicColor, Float.isNaN(state.rules.dynamicColor.a) ? 0.5f : Math.max(0.5f, state.rules.dynamicColor.a)); Draw.fbo(dynamicFog.getTexture(), world.width(), world.height(), tilesize); //TODO ai check? if(state.rules.staticFog){ //TODO why does this require a half-tile offset while dynamic does not Draw.color(state.rules.staticColor, 1f); Draw.fbo(staticFog.getTexture(), world.width(), world.height(), tilesize, tilesize/2f); } Draw.shader(); } void poly(float x, float y, float rad){ Fill.poly(x, y, 20, rad); } void renderEvent(long e){ Tile tile = world.tile(FogEvent.x(e), FogEvent.y(e)); float o = 0f; //visual offset for uneven blocks; this is not reflected on the CPU, but it doesn't really matter if(tile != null && tile.block().size % 2 == 0 && tile.isCenter()){ o = 0.5f; } Fill.poly(FogEvent.x(e) + 0.5f + o, FogEvent.y(e) + 0.5f + o, 20, FogEvent.radius(e) + 0.3f); } public void copyFromCpu(){ staticFog.resize(world.width(), world.height()); staticFog.begin(Color.black); Draw.proj(0, 0, staticFog.getWidth(), staticFog.getHeight()); Draw.color(); int ww = world.width(), wh = world.height(); var data = fogControl.getDiscovered(player.team()); int len = world.width() * world.height(); if(data != null){ for(int i = 0; i < len; i++){ if(data.get(i)){ //TODO slow, could do scanlines instead at the very least. int x = i % ww, y = i / ww; //manually clip with 1 pixel of padding so the borders are never fully revealed if(x > 0 && y > 0 && x < ww - 1 && y < wh - 1){ Fill.rect(x + 0.5f, y + 0.5f, 1f, 1f); } } } } staticFog.end(); Draw.proj(Core.camera); } }