package net.fourbytes.shadow;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.BitmapFont.TextBounds;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.utils.Array;
import net.fourbytes.shadow.entities.Cursor;
import net.fourbytes.shadow.entities.Player;
import net.fourbytes.shadow.systems.ILightSystem;
import net.fourbytes.shadow.systems.LightSystemHelper;
import net.fourbytes.shadow.utils.ChunkRenderer;
import net.fourbytes.shadow.utils.Garbage;
import net.fourbytes.shadow.utils.ShaderHelper;
public class Camera {
public Background bg;
public OrthographicCamera cam;
public static BitmapFont fpsFont = Fonts.light_normal;
public static boolean fpsBoundsUpdate = true;
public static TextBounds fpsBoundMain;
public static String fpsTextMain = "FPS:";
protected static Rectangle objrec = new Rectangle();
protected static TextureRegion white;
protected Player player;
public boolean level = true;
public boolean firsttick = true;
public Rectangle camrec = new Rectangle(0f, 0f, 0f, 0f);
public Vector2 offs = new Vector2(0f, 0f);
public float inviewf = 2f;
public static ChunkRenderer chunkrenderer;
public static FrameBuffer tmpFB;
public static FrameBuffer blurFB;
public static FrameBuffer blurXFB;
/**
* Requires Shadow.resize() to update FB.
*/
public static float blursize = 2f;
public static int blurrad = 4;
public static float blurdist = 0.1f;
public static boolean blur;
public static boolean blurHD;
public static boolean blurLight;
public static boolean shadows;
public static boolean shadowsCheck;
public static boolean multiblend;
public static boolean showRAM;
public Camera() {
this.cam = new OrthographicCamera(Shadow.vieww, -Shadow.viewh);
this.cam.position.set(0f, 0f, 0f);
this.cam.update();
}
public void resize() {
cam.viewportWidth = Shadow.vieww;
cam.viewportHeight = -Shadow.viewh;
cam.update();
}
public void render(float delta) {
if (white == null) {
white = Images.getTextureRegion("white");
}
if (!(Shadow.level instanceof MenuLevel) && !(Shadow.level instanceof LoadingLevel)) {
player = Shadow.level.player;
}
float goalx;
float goaly;
if (player == null) {
goalx = 0f;
goaly = 0f;
} else {
goalx = player.pos.x + player.rec.width/2f;
goaly = player.pos.y + player.rec.height/2f;
}
if (firsttick) {
cam.zoom = 1f;
cam.position.x = goalx;
cam.position.y = goaly;
firsttick = false;
}
cam.position.x += ((goalx-cam.position.x)/15f)*(delta*60f);
cam.position.y += ((goaly-cam.position.y)/20f)*(delta*60f);
offs.set(goalx - cam.position.x, goaly - cam.position.y);
cam.update();
camrec.set(cam.position.x, cam.position.y, cam.viewportWidth*cam.zoom, -cam.viewportHeight*cam.zoom);
camrec.x -= camrec.width/2f;
camrec.y -= camrec.height/2f;
ShaderHelper.set("s_viewport", (cam.position.x/cam.viewportWidth)*Shadow.dispw, (cam.position.y/cam.viewportHeight)*Shadow.disph);
Shadow.spriteBatch.setProjectionMatrix(cam.combined);
Shadow.spriteBatch.maxSpritesInBatch = 0;
Shadow.spriteBatch.begin();
if (bg == null) {
bg = Background.getDefault();
}
bg.render();
renderLevel(Shadow.level);
//TODO REMOVE THIS AND FOLLOWING LINES
//Shadow.spriteBatch.draw(chunkrenderer.tex, camrec.x, camrec.y, camrec.width, camrec.height);
if (showRAM) {
float width = camrec.width / Shadow.ramLogMax;
double ramTotal = 0f;
for (int i = 0; i < Shadow.ramTotal.size; i++) {
if (Shadow.ramTotal.items[i] > ramTotal) {
ramTotal = (double) Shadow.ramTotal.items[i];
}
}
for (int i = 0; i < Shadow.ramUsed.size; i++) {
double ramUsed = (double) Shadow.ramUsed.items[i];
float height = (float) (ramUsed / ramTotal) * camrec.height;
float r = 1f - ((float) i / (float) Shadow.ramLogMax);
Shadow.spriteBatch.setColor(r, r, r, 0.5f);
Shadow.spriteBatch.draw(white, camrec.x + camrec.width - i*width - width, camrec.y + camrec.height, width, -height);
}
}
if (fpsFont == null) {
fpsFont = Fonts.light_normal;
fpsBoundsUpdate = true;
}
if (fpsFont != null) {
//fpsFont.setScale(Shadow.vieww/Shadow.dispw * cam.zoom, -Shadow.viewh/Shadow.disph * cam.zoom);
//fpsFont.draw(Shadow.spriteBatch, "FPS: "+Shadow.fps, cam.position.x - Shadow.vieww/2 * cam.zoom, cam.position.y - Shadow.viewh/2 * cam.zoom);
fpsFont.setScale(Shadow.vieww/Shadow.dispw, -Shadow.viewh/Shadow.disph);
if (fpsBoundsUpdate) {
fpsBoundMain = new TextBounds(fpsFont.getBounds(fpsTextMain));
}
String fpsTextFPS = Garbage.getStringForInt(Shadow.fps);
TextBounds fpsBoundFPS = fpsFont.getBounds(fpsTextFPS);
fpsFont.draw(Shadow.spriteBatch, fpsTextFPS,
cam.position.x + Shadow.vieww/2f - fpsBoundFPS.width,
cam.position.y - Shadow.viewh/2f);
fpsFont.draw(Shadow.spriteBatch, fpsTextMain,
cam.position.x + Shadow.vieww/2f - fpsBoundMain.width - fpsFont.getSpaceWidth() - fpsBoundFPS.width,
cam.position.y - Shadow.viewh/2f);
if (showRAM) {
String fpsTextMEM = Double.toString(Shadow.ramUsed.items[0]/1000000D) + "MB";
fpsFont.draw(Shadow.spriteBatch, fpsTextMEM,
cam.position.x - Shadow.vieww / 2f,
cam.position.y - Shadow.viewh / 2f);
}
}
Shadow.spriteBatch.flush();
Shadow.spriteBatch.setProjectionMatrix(Input.cam.combined);
Input.render();
Shadow.spriteBatch.setProjectionMatrix(cam.combined);
Shadow.spriteBatch.end();
//To disable / enable debugging, just add / remove "/*" to / from the beginning of this line.
/*System.out.println("(Camera) max sprites in batch: "+Shadow.spriteBatch.maxSpritesInBatch);
System.out.println("(Camera) render calls: "+Shadow.spriteBatch.renderCalls);
Shadow.spriteBatch.maxSpritesInBatch = 0;
/*
*/
}
public void renderLevel(Level level) {
if (!(level instanceof MenuLevel)) {
if (blurLight && LightSystemHelper.lightFB != null) {
tmpFB.begin();
} else if (blur && !this.level) {
blurFB.begin();
}
for (Layer ll : level.layers.values()) {
renderLayer(ll);
}
ILightSystem lights = level.systems.get(ILightSystem.class);
if (lights != null) {
LightSystemHelper.updateLightBounds();
lights.renderFBO();
}
if ((blur && !this.level) || (blurLight && LightSystemHelper.lightFB != null)) {
Shadow.spriteBatch.flush();
if (blurLight && LightSystemHelper.lightFB != null) {
tmpFB.end();
if (!blur || this.level) {
Shadow.spriteBatch.disableBlending();
Shadow.spriteBatch.draw(tmpFB.getColorBufferTexture(),
camrec.x, camrec.y, camrec.width, camrec.height);
Shadow.spriteBatch.enableBlending();
}
blurFB.begin();
Shadow.spriteBatch.disableBlending();
Shadow.spriteBatch.draw(tmpFB.getColorBufferTexture(),
camrec.x, camrec.y, camrec.width, camrec.height);
Shadow.spriteBatch.enableBlending();
blurFB.end();
} else if (blur && !this.level) {
blurFB.end();
}
if (blurHD) {
float a;
blurXFB.begin();
Shadow.spriteBatch.setColor(1f, 1f, 1f, 1f);
Shadow.spriteBatch.draw(blurFB.getColorBufferTexture(),
camrec.x, camrec.y, camrec.width, camrec.height);
for (float x = blurrad-1f; x > 0; x--) {
a = 1f - (x / (float)blurrad);
Shadow.spriteBatch.setColor(1f, 1f, 1f, a / 2f);
Shadow.spriteBatch.draw(blurFB.getColorBufferTexture(),
camrec.x - x * blurdist, camrec.y, camrec.width, camrec.height);
Shadow.spriteBatch.draw(blurFB.getColorBufferTexture(),
camrec.x + x * blurdist, camrec.y, camrec.width, camrec.height);
}
Shadow.spriteBatch.flush();
blurXFB.end();
blurFB.begin();
Shadow.spriteBatch.setColor(1f, 1f, 1f, 1f);
Shadow.spriteBatch.draw(blurXFB.getColorBufferTexture(),
camrec.x, camrec.y, camrec.width, camrec.height);
for (float y = blurrad-1f; y > 0; y--) {
a = 1f - (y / (float)blurrad);
Shadow.spriteBatch.setColor(1f, 1f, 1f, a / 2f);
Shadow.spriteBatch.draw(blurXFB.getColorBufferTexture(),
camrec.x, camrec.y - y * blurdist, camrec.width, camrec.height);
Shadow.spriteBatch.draw(blurXFB.getColorBufferTexture(),
camrec.x, camrec.y + y * blurdist, camrec.width, camrec.height);
}
Shadow.spriteBatch.flush();
blurFB.end();
}
if (Shadow.level instanceof MenuLevel) {
Color c = ((MenuLevel) Shadow.level).dimm;
Shadow.spriteBatch.setColor(1f - (1f - c.r) * c.a,
1f - (1f - c.g) * c.a,
1f - (1f - c.b) * c.a, 1f);
} else {
Shadow.spriteBatch.setColor(1f, 1f, 1f, 1f);
}
if (blur && !this.level) {
Shadow.spriteBatch.disableBlending();
Shadow.spriteBatch.draw(blurFB.getColorBufferTexture(),
camrec.x, camrec.y, camrec.width, camrec.height);
Shadow.spriteBatch.enableBlending();
}
if (blurLight && LightSystemHelper.lightFB != null) {
Shadow.spriteBatch.flush();
String shaderOld = ShaderHelper.getCurrentShaderName();
ShaderHelper.setCurrentShader("blurlight");
ShaderHelper.set("u_light", 1);
float lightIntensity = (level.globalLight.r + level.globalLight.g + level.globalLight.b) / 3f;
lightIntensity = lightIntensity * lightIntensity * lightIntensity * lightIntensity;
ShaderHelper.set("u_lightBlurIntensity", 1f - lightIntensity);
LightSystemHelper.lightFB.getColorBufferTexture().bind(1);
Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0);
Shadow.spriteBatch.draw(blurFB.getColorBufferTexture(),
camrec.x, camrec.y, camrec.width, camrec.height);
Shadow.spriteBatch.flush();
LightSystemHelper.lightFB.getColorBufferTexture().bind(0);
ShaderHelper.setCurrentShader(shaderOld);
}
Shadow.spriteBatch.setColor(1f, 1f, 1f, 1f);
}
}
if (this.level) {
for (Cursor c : level.cursors) {
c.preRender();
c.render();
}
if (level.c != null && !Input.isAndroid) {
level.c.preRender();
level.c.render();
}
level.renderImpl();
}
}
public void renderLayer(Layer l) {
if (l == null) {
return;
}
Array<GameObject> gos = l.inView;
if (chunkrenderer != null) {
chunkrenderer.render(l);
}
boolean prerendered;
if (shadows) {
renderShadows(l, shadowsCheck);
prerendered = true;
} else {
prerendered = false;
}
boolean multiblended;
if (multiblend) {
renderObjects(l, false, false, prerendered);
renderObjects(l, false, true, prerendered);
renderObjects(l, true, false, prerendered);
renderObjects(l, true, true, prerendered);
multiblended = true;
} else {
renderObjects(gos, false, prerendered);
renderObjects(gos, true, prerendered);
multiblended = false;
}
if (multiblended) {
Shadow.spriteBatch.enableBlending();
}
}
protected void renderShadows(Layer l, boolean doCheck) {
Array<GameObject> gos = l.inView;
float ox, oy, ow, oh;
for (int i = 0; i < gos.size; i++) {
GameObject go = gos.items[i];
if (go == null) continue;
if (chunkrenderer != null && go instanceof Block && !((Block)go).dynamic) {
continue;
}
ox = go.pos.x-inviewf;
oy = go.pos.y-inviewf;
ow = go.rec.width+inviewf*2f;
oh = go.rec.height+inviewf*2f;
if (!(camrec.x < ox + ow && camrec.x + camrec.width > ox &&
camrec.y < oy + oh && camrec.y + camrec.height > oy)) {
continue;
}
go.preRender();
if (doCheck && go instanceof Block && !go.blending) {
int taken = 0;
Array<Block> blocks;
blocks = go.layer.get(Coord.get(go.pos.x + 1, go.pos.y));
if (blocks != null && blocks.size > 0) {
for (int ii = 0; ii < blocks.size; ii++) {
Block b = blocks.items[ii];
if (!b.blending) {
taken++;
break;
}
}
blocks = go.layer.get(Coord.get(go.pos.x, go.pos.y + 1));
if (blocks != null && blocks.size > 0 && taken == 1) {
for (int ii = 0; ii < blocks.size; ii++) {
Block b = blocks.items[ii];
if (!b.blending) {
taken++;
break;
}
}
}
blocks = go.layer.get(Coord.get(go.pos.x + 1, go.pos.y + 1));
if (blocks != null && blocks.size > 0 && taken == 2) {
for (int ii = 0; ii < blocks.size; ii++) {
Block b = blocks.items[ii];
if (!b.blending) {
taken++;
break;
}
}
}
if (taken == 3) {
continue;
}
}
}
Image img = go.images[go.imgIDs[0]]; //TODO Render all images' shadows.
if (img != null) {
Shadow.spriteBatch.setColor(0f, 0f, 0f, go.alpha*img.getColor().a*0.5f);
img.getDrawable().draw(Shadow.spriteBatch, 0.125f + go.pos.x + go.renderoffs.x,
0.125f + go.pos.y + go.rec.height + go.renderoffs.y,
go.rec.width + go.renderoffs.width, -go.rec.height + go.renderoffs.height);
}
}
}
protected void renderObjects(Layer l, boolean fgonly, boolean blending, boolean prerendered) {
Array<GameObject> gos = l.inView;
float ox, oy, ow, oh;
boolean blendingSwitched = false;
for (int i = 0; i < gos.size; i++) {
GameObject go = gos.items[i];
if (go == null) continue;
if (chunkrenderer != null && go instanceof Block && !((Block)go).dynamic) {
continue;
}
if (go.alpha < 0.05f) continue;
ox = go.pos.x-inviewf;
oy = go.pos.y-inviewf;
ow = go.rec.width+inviewf*2f;
oh = go.rec.height+inviewf*2f;
if (!(camrec.x < ox + ow && camrec.x + camrec.width > ox &&
camrec.y < oy + oh && camrec.y + camrec.height > oy)) {
continue;
}
if (go.blending != blending) {
continue;
}
if (go instanceof Block) {
if (fgonly && ((Block)go).rendertop == 0x00) {
continue;
} else if (!fgonly && ((Block)go).rendertop == 0x01) {
continue;
}
} else if (fgonly) {
continue;
}
if (!prerendered) {
go.preRender();
}
if (!blendingSwitched) {
if (go.blending) {
Shadow.spriteBatch.enableBlending();
blendingSwitched = true;
} else {
Shadow.spriteBatch.disableBlending();
blendingSwitched = true;
}
}
if (go instanceof Block) {
if (fgonly) {
((Block)go).renderTop();
} else {
go.render();
}
} else {
go.render();
}
}
}
protected void renderObjects(Array<GameObject> gos, boolean fgonly, boolean prerendered) {
float ox, oy, ow, oh;
for (int i = 0; i < gos.size; i++) {
GameObject go = gos.items[i];
if (go == null) continue;
if (chunkrenderer != null && go instanceof Block && !((Block)go).dynamic) {
continue;
}
ox = go.pos.x-inviewf;
oy = go.pos.y-inviewf;
ow = go.rec.width+inviewf*2f;
oh = go.rec.height+inviewf*2f;
if (!(camrec.x < ox + ow && camrec.x + camrec.width > ox &&
camrec.y < oy + oh && camrec.y + camrec.height > oy)) {
continue;
}
if (go instanceof Block) {
if (fgonly && ((Block)go).rendertop == 0x00) {
continue;
} else if (!fgonly && ((Block)go).rendertop == 0x01) {
continue;
}
} else if (fgonly) {
continue;
}
if (!prerendered) {
go.preRender();
}
if (go instanceof Block) {
if (fgonly) {
((Block)go).renderTop();
} else {
go.render();
}
} else {
go.render();
}
}
}
}