#include "stdafx.h"
#include "GameRenderer.h"
#include "ItemInHandRenderer.h"
#include "LevelRenderer.h"
#include "Frustum.h"
#include "FrustumCuller.h"
#include "Textures.h"
#include "Tesselator.h"
#include "ParticleEngine.h"
#include "SmokeParticle.h"
#include "WaterDropParticle.h"
#include "GameMode.h"
#include "CreativeMode.h"
#include "Lighting.h"
#include "Options.h"
#include "MultiplayerLocalPlayer.h"
#include "GuiParticles.h"
#include "MultiPlayerLevel.h"
#include "Chunk.h"
#include "..\Minecraft.World\net.minecraft.world.entity.h"
#include "..\Minecraft.World\net.minecraft.world.level.h"
#include "..\Minecraft.World\net.minecraft.world.entity.player.h"
#include "..\Minecraft.World\net.minecraft.world.phys.h"
#include "..\Minecraft.World\net.minecraft.world.level.material.h"
#include "..\Minecraft.World\net.minecraft.world.level.tile.h"
#include "..\Minecraft.World\net.minecraft.world.level.chunk.h"
#include "..\Minecraft.World\net.minecraft.world.level.biome.h"
#include "..\Minecraft.World\net.minecraft.world.level.dimension.h"
#include "..\Minecraft.World\System.h"
#include "..\Minecraft.World\FloatBuffer.h"
#include "..\Minecraft.World\ThreadName.h"
#include "..\Minecraft.World\SparseLightStorage.h"
#include "..\Minecraft.World\CompressedTileStorage.h"
#include "..\Minecraft.World\SparseDataStorage.h"
#include "..\Minecraft.World\JavaMath.h"
#include "..\Minecraft.World\Facing.h"
#include "..\Minecraft.World\MobEffect.h"
#include "..\Minecraft.World\IntCache.h"
#include "..\Minecraft.World\SmoothFloat.h"
#include "..\Minecraft.World\MobEffectInstance.h"
#include "..\Minecraft.World\Item.h"
#include "Camera.h"
#include "..\Minecraft.World\SoundTypes.h"
#include "HumanoidModel.h"
#include "..\Minecraft.World\Item.h"
#include "..\Minecraft.World\compression.h"
#include "PS3\PS3Extras\ShutdownManager.h"

#include "TexturePackRepository.h"
#include "TexturePack.h"

bool GameRenderer::anaglyph3d = false;
int GameRenderer::anaglyphPass = 0;

#ifdef MULTITHREAD_ENABLE
C4JThread*		GameRenderer::m_updateThread;
C4JThread::EventArray* GameRenderer::m_updateEvents;
bool GameRenderer::nearThingsToDo = false;
bool GameRenderer::updateRunning = false;
vector<byte *> GameRenderer::m_deleteStackByte;
vector<SparseLightStorage *> GameRenderer::m_deleteStackSparseLightStorage;
vector<CompressedTileStorage *> GameRenderer::m_deleteStackCompressedTileStorage;
vector<SparseDataStorage *> GameRenderer::m_deleteStackSparseDataStorage;
#endif
CRITICAL_SECTION GameRenderer::m_csDeleteStack;

GameRenderer::GameRenderer(Minecraft *mc)
{
	// 4J - added this block of initialisers
	renderDistance = 0;
	_tick = 0;
	hovered = nullptr;
	thirdDistance = 4;
	thirdDistanceO = 4;
	thirdRotation = 0;
	thirdRotationO = 0;
	thirdTilt = 0;
	thirdTiltO = 0;

    accumulatedSmoothXO = 0;
	accumulatedSmoothYO = 0;
    tickSmoothXO = 0;
	tickSmoothYO = 0;
	lastTickA = 0;

	cameraPos = Vec3::newPermanent(0.0f,0.0f,0.0f);

	fovOffset = 0;
	fovOffsetO = 0;
	cameraRoll = 0;
	cameraRollO = 0;
	for( int i = 0; i < 4; i++ )
	{
		fov[i] = 0.0f;
		oFov[i] = 0.0f;
		tFov[i] = 0.0f;
	}
	isInClouds = false;
	zoom = 1;
	zoom_x = 0;
	zoom_y = 0;
	rainXa = NULL;
	rainZa = NULL;
	lastActiveTime = Minecraft::currentTimeMillis();
	lastNsTime = 0;
	random = new Random();
	rainSoundTime = 0;
	xMod = 0;
	yMod = 0;
	lb = MemoryTracker::createFloatBuffer(16);
	fr = 0.0f;
	fg = 0.0f;
	fb = 0.0f;
	fogBrO = 0.0f;
	fogBr = 0.0f;
	cameraFlip = 0;
	_updateLightTexture = false;
	blr = 0.0f;
	blrt = 0.0f;
	blg = 0.0f;
	blgt = 0.0f;


	m_fov=70.0f;

	// 4J Stu - Init these so they are setup before the tick
	for( int i = 0; i < 4; i++ )
	{
		fov[i] = oFov[i] = 1.0f;
	}

	this->mc = mc;
	itemInHandRenderer = NULL;

	// 4J-PB - set up the local players iteminhand renderers here - needs to be done with lighting enabled so that the render geometry gets compiled correctly
	glEnable(GL_LIGHTING);
	mc->localitemInHandRenderers[0] = new ItemInHandRenderer(mc);//itemInHandRenderer;
	mc->localitemInHandRenderers[1] = new ItemInHandRenderer(mc);
	mc->localitemInHandRenderers[2] = new ItemInHandRenderer(mc);
	mc->localitemInHandRenderers[3] = new ItemInHandRenderer(mc);
	glDisable(GL_LIGHTING);

	// 4J - changes brought forward from 1.8.2
	BufferedImage *img = new BufferedImage(16, 16, BufferedImage::TYPE_INT_RGB);
	for( int i = 0; i < NUM_LIGHT_TEXTURES; i++ )
	{
		lightTexture[i] = mc->textures->getTexture(img);		// 4J - changed to one light texture per level to support split screen
	}
	delete img;
#ifdef __PS3__
	// we're using the RSX now to upload textures to vram, so we need the main ram textures allocated from io space
	for(int i=0;i<NUM_LIGHT_TEXTURES;i++)
		lightPixels[i] = intArray((int*)RenderManager.allocIOMem(16*16*sizeof(int)), 16*16);
#else
	for(int i=0;i<NUM_LIGHT_TEXTURES;i++)
		lightPixels[i] = intArray(16*16);
#endif

#ifdef MULTITHREAD_ENABLE
	m_updateEvents  = new C4JThread::EventArray(eUpdateEventCount, C4JThread::EventArray::e_modeAutoClear);
	m_updateEvents->Set(eUpdateEventIsFinished);

	InitializeCriticalSection(&m_csDeleteStack);
	m_updateThread = new C4JThread(runUpdate, NULL, "Chunk update");
#ifdef __PS3__
	m_updateThread->SetPriority(THREAD_PRIORITY_ABOVE_NORMAL);
#endif// __PS3__
	m_updateThread->SetProcessor(CPU_CORE_CHUNK_UPDATE);
	m_updateThread->Run();
#endif
}

// 4J Stu Added to go with 1.8.2 change
GameRenderer::~GameRenderer()
{
	if(rainXa != NULL) delete [] rainXa;
	if(rainZa != NULL) delete [] rainZa;
}

void GameRenderer::tick(bool first)		// 4J - add bFirst
{ 
	tickFov();
	tickLightTexture();		// 4J - change brought forward from 1.8.2
	fogBrO = fogBr;
	thirdDistanceO = thirdDistance;
	thirdRotationO = thirdRotation;
	thirdTiltO = thirdTilt;
	fovOffsetO = fovOffset;
	cameraRollO = cameraRoll;

	if (ClientConstants::DEADMAU5_CAMERA_CHEATS)
	{
		if (mc->screen == NULL)
		{
			float distanceDelta = 0;
			float rotationDelta = 0;
			float tiltDelta = 0;
			float rollDelta = 0;

			if (Keyboard::isKeyDown(Keyboard::KEY_U))
			{
				distanceDelta -= .3f * mc->options->cameraSpeed;
			}
			else if (Keyboard::isKeyDown(Keyboard::KEY_O))
			{
				distanceDelta += .3f * mc->options->cameraSpeed;
			}
			if (Keyboard::isKeyDown(Keyboard::KEY_J))
			{
				rotationDelta += 8.0f * mc->options->cameraSpeed;
			}
			else if (Keyboard::isKeyDown(Keyboard::KEY_L))
			{
				rotationDelta -= 8.0f * mc->options->cameraSpeed;
			}
			if (Keyboard::isKeyDown(Keyboard::KEY_I))
			{
				tiltDelta += 6.0f * mc->options->cameraSpeed;
			}
			else if (Keyboard::isKeyDown(Keyboard::KEY_K))
			{
				tiltDelta -= 6.0f * mc->options->cameraSpeed;
			}
			if (Keyboard::isKeyDown(Keyboard::KEY_Y) && Keyboard::isKeyDown(Keyboard::KEY_H))
			{
				fovOffset = 0;
			}
			else if (Keyboard::isKeyDown(Keyboard::KEY_Y))
			{
				fovOffset -= 3.0f;
			}
			else if (Keyboard::isKeyDown(Keyboard::KEY_H))
			{
				fovOffset += 3.0f;
			}
			if (Keyboard::isKeyDown(Keyboard::KEY_N) && Keyboard::isKeyDown(Keyboard::KEY_M))
			{
				cameraRoll = 0;
			}
			else if (Keyboard::isKeyDown(Keyboard::KEY_N))
			{
				rollDelta -= 8.0f * mc->options->cameraSpeed;
			}
			else if (Keyboard::isKeyDown(Keyboard::KEY_M))
			{
				rollDelta += 8.0f * mc->options->cameraSpeed;
			}

			if (mc->options->smoothCamera)
			{
				distanceDelta = smoothDistance.getNewDeltaValue(distanceDelta, .5f * mc->options->sensitivity);
				rotationDelta = smoothRotation.getNewDeltaValue(rotationDelta, .5f * mc->options->sensitivity);
				tiltDelta = smoothTilt.getNewDeltaValue(tiltDelta, .5f * mc->options->sensitivity);
				rollDelta = smoothRoll.getNewDeltaValue(rollDelta, .5f * mc->options->sensitivity);
			}
			thirdDistance += distanceDelta;
			thirdRotation += rotationDelta;
			thirdTilt += tiltDelta;
			cameraRoll += rollDelta;
		}
	}

    if (mc->options->smoothCamera)
	{
        // update player view in tick() instead of render() to maintain
// camera movement regardless of FPS
        float ss = mc->options->sensitivity * 0.6f + 0.2f;
        float sens = (ss * ss * ss) * 8;
        tickSmoothXO = smoothTurnX.getNewDeltaValue(accumulatedSmoothXO, 0.05f * sens);
        tickSmoothYO = smoothTurnY.getNewDeltaValue(accumulatedSmoothYO, 0.05f * sens);
        lastTickA = 0;

        accumulatedSmoothXO = 0;
        accumulatedSmoothYO = 0;
    }

	if (mc->cameraTargetPlayer == NULL)
	{
		mc->cameraTargetPlayer = dynamic_pointer_cast<Mob>(mc->player);
	}

	float brr = mc->level->getBrightness(Mth::floor(mc->cameraTargetPlayer->x), Mth::floor(mc->cameraTargetPlayer->y), Mth::floor(mc->cameraTargetPlayer->z));
	float whiteness = (3 - mc->options->viewDistance) / 3.0f;
	float fogBrT = brr * (1 - whiteness) + whiteness;
	fogBr += (fogBrT - fogBr) * 0.1f;

	itemInHandRenderer->tick();

	PIXBeginNamedEvent(0,"Rain tick");
	tickRain();
	PIXEndNamedEvent();

	if( mc->player != mc->localplayers[ProfileManager.GetPrimaryPad()] ) return; // 4J added for split screen - only do rest of processing for once per frame

	_tick++;
}

void GameRenderer::pick(float a)
{
	if (mc->cameraTargetPlayer == NULL) return;
	if (mc->level == NULL) return;

	double range = mc->gameMode->getPickRange();
	delete mc->hitResult;
	MemSect(31);
	mc->hitResult = mc->cameraTargetPlayer->pick(range, a);
	MemSect(0);

	// 4J - added - stop blocks right at the edge of the world from being pickable so we shouldn't be able to directly destroy or create anything there
	if( mc->hitResult )
	{
		int maxxz = ( ( mc->level->chunkSource->m_XZSize / 2 ) * 16 )  - 2;
		int minxz = ( -( mc->level->chunkSource->m_XZSize / 2 ) * 16 ) + 1;

		// Don't select the tops of the very edge blocks, or the sides of the next blocks in
		// 4J Stu - Only block the sides that are facing an outside block
		int hitx = mc->hitResult->x;
		int hitz = mc->hitResult->z;
		int face = mc->hitResult->f;
		if( face == Facing::WEST && hitx < 0 ) hitx -= 1;
		if( face == Facing::EAST && hitx > 0 ) hitx += 1;
		if( face == Facing::NORTH && hitz < 0 ) hitz -= 1;
		if( face == Facing::SOUTH && hitz > 0 ) hitz += 1;

		if( ( hitx < minxz ) || ( hitx > maxxz) ||
			( hitz < minxz ) || ( hitz > maxxz) )
		{
			delete mc->hitResult;
			mc->hitResult = NULL;
		}
	}

	double dist = range;
	Vec3 *from = mc->cameraTargetPlayer->getPos(a);

	if (mc->gameMode->hasFarPickRange())
	{
		dist = range = 6;
	}
	else
	{
		if (dist > 3) dist = 3;
		range = dist;
	}

	if (mc->hitResult != NULL)
	{
		dist = mc->hitResult->pos->distanceTo(from);
	}

    Vec3 *b = mc->cameraTargetPlayer->getViewVector(a);
    Vec3 *to = from->add(b->x * range, b->y * range, b->z * range);
    hovered = nullptr;
    float overlap = 1;
    vector<shared_ptr<Entity> > *objects = mc->level->getEntities(mc->cameraTargetPlayer, mc->cameraTargetPlayer->bb->expand(b->x * (range), b->y * (range), b->z * (range))->grow(overlap, overlap, overlap));
    double nearest = dist;

	AUTO_VAR(itEnd, objects->end());
	for (AUTO_VAR(it, objects->begin()); it != itEnd; it++)
	{
        shared_ptr<Entity> e = *it; //objects->at(i);
        if (!e->isPickable()) continue;

		float rr = e->getPickRadius();
		AABB *bb = e->bb->grow(rr, rr, rr);
		HitResult *p = bb->clip(from, to);
		if (bb->contains(from))
		{
			if (0 < nearest || nearest == 0)
			{
				hovered = e;
				nearest = 0;
			}
		}
		else if (p != NULL)
		{
			double dd = from->distanceTo(p->pos);
			if (dd < nearest || nearest == 0)
			{
				hovered = e;
				nearest = dd;
			}
		}
		delete p;
	}

	if (hovered != NULL)
	{
		if (nearest < dist || (mc->hitResult == NULL))
		{
			if( mc->hitResult != NULL )
				delete mc->hitResult;
			mc->hitResult = new HitResult(hovered);
		}
	}
}

void GameRenderer::SetFovVal(float fov)
{
	m_fov=fov;
}

float GameRenderer::GetFovVal()
{
	return m_fov;
}

void GameRenderer::tickFov()
{
	shared_ptr<LocalPlayer>player = dynamic_pointer_cast<LocalPlayer>(mc->cameraTargetPlayer);

	int playerIdx = player ? player->GetXboxPad() : 0;
    tFov[playerIdx] = player->getFieldOfViewModifier();

    oFov[playerIdx] = fov[playerIdx];
    fov[playerIdx] += (tFov[playerIdx] - fov[playerIdx]) * 0.5f;
}

float GameRenderer::getFov(float a, bool applyEffects)
{
	if (cameraFlip > 0 ) return 90;

	shared_ptr<LocalPlayer> player = dynamic_pointer_cast<LocalPlayer>(mc->cameraTargetPlayer);
	int playerIdx = player ? player->GetXboxPad() : 0;
	float fov = m_fov;//70;
    if (applyEffects)
	{
        fov += mc->options->fov * 40;
        fov *= this->oFov[playerIdx] + (this->fov[playerIdx] - this->oFov[playerIdx]) * a;
    }
	if (player->getHealth() <= 0)
	{
		float duration = player->deathTime + a;

		fov /= ((1 - 500 / (duration + 500)) * 2.0f + 1);
	}

    int t = Camera::getBlockAt(mc->level, player, a);
    if (t != 0 && Tile::tiles[t]->material == Material::water) fov = fov * 60 / 70;

	return fov + fovOffsetO + (fovOffset - fovOffsetO) * a;

}

void GameRenderer::bobHurt(float a)
{
	shared_ptr<Mob> player = mc->cameraTargetPlayer;

	float hurt = player->hurtTime - a;

	if (player->getHealth() <= 0)
	{
		float duration = player->deathTime + a;

		glRotatef(40 - (40 * 200) / (duration + 200), 0, 0, 1);
	}

	if (hurt < 0) return;
	hurt /= player->hurtDuration;
	hurt = (float) Mth::sin(hurt * hurt * hurt * hurt * PI);

	float rr = player->hurtDir;


	glRotatef(-rr, 0, 1, 0);
	glRotatef(-hurt * 14, 0, 0, 1);
	glRotatef(+rr, 0, 1, 0);

}

void GameRenderer::bobView(float a)
{
	shared_ptr<Player> player = dynamic_pointer_cast<Player>(mc->cameraTargetPlayer);
	if(player==NULL)
	{
		return;
	}
	//shared_ptr<Player> player = dynamic_pointer_cast<Player>(mc->cameraTargetPlayer);

	float wda = player->walkDist - player->walkDistO;
	float b = -(player->walkDist + wda * a);
	float bob = player->oBob + (player->bob - player->oBob) * a;
	float tilt = player->oTilt + (player->tilt - player->oTilt) * a;
	glTranslatef((float) Mth::sin(b * PI) * bob * 0.5f, -(float) abs(Mth::cos(b * PI) * bob), 0);
	glRotatef((float) Mth::sin(b * PI) * bob * 3, 0, 0, 1);
	glRotatef((float) abs(Mth::cos(b * PI - 0.2f) * bob) * 5, 1, 0, 0);
	glRotatef((float) tilt, 1, 0, 0);
}

void GameRenderer::moveCameraToPlayer(float a)
{
	shared_ptr<Mob> player = mc->cameraTargetPlayer;
	shared_ptr<LocalPlayer> localplayer = dynamic_pointer_cast<LocalPlayer>(mc->cameraTargetPlayer);
	float heightOffset = player->heightOffset - 1.62f;

	double x = player->xo + (player->x - player->xo) * a;
	double y = player->yo + (player->y - player->yo) * a - heightOffset;
	double z = player->zo + (player->z - player->zo) * a;


	glRotatef(cameraRollO + (cameraRoll - cameraRollO) * a, 0, 0, 1);

	if (player->isSleeping())
	{
		heightOffset += 1.0;
		glTranslatef(0.0f, 0.3f, 0);
		if (!mc->options->fixedCamera)
		{
			int t = mc->level->getTile(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z));
			if (t == Tile::bed_Id)
			{
				int data = mc->level->getData(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z));

				int direction = data & 3;
				glRotatef((float)direction * 90,0.0f, 1.0f, 0.0f);
			}
			glRotatef(player->yRotO + (player->yRot - player->yRotO) * a + 180, 0, -1, 0);
			glRotatef(player->xRotO + (player->xRot - player->xRotO) * a, -1, 0, 0);
		}
	}
	// 4J-PB - changing this to be per player
	//else if (mc->options->thirdPersonView)
	else if (localplayer->ThirdPersonView())
	{
		double cameraDist = thirdDistanceO + (thirdDistance - thirdDistanceO) * a;

		if (mc->options->fixedCamera)
		{

			float rotationY = thirdRotationO + (thirdRotation - thirdRotationO) * a;
			float xRot = thirdTiltO + (thirdTilt - thirdTiltO) * a;

			glTranslatef(0, 0, (float) -cameraDist);
			glRotatef(xRot, 1, 0, 0);
			glRotatef(rotationY, 0, 1, 0);
		}
		else
		{
			// 4J - corrected bug where this used to just take player->xRot & yRot directly and so wasn't taking into account interpolation, allowing camera to go through walls
			float yRot = player->yRotO + (player->yRot - player->yRotO) * a;
			float xRot = player->xRotO + (player->xRot - player->xRotO) * a;

			// Thirdperson view values are now 0 for disabled, 1 for original mode, 2 for reversed.
			if( localplayer->ThirdPersonView() == 2 )
			{
				// Reverse y rotation - note that this is only used in doing collision to calculate our view
				// distance, the actual rotation itself is just below this else {} block
				yRot += 180.0f;
			}

			double xd = -Mth::sin(yRot / 180 * PI) * Mth::cos(xRot / 180 * PI) * cameraDist;
			double zd = Mth::cos(yRot / 180 * PI) * Mth::cos(xRot / 180 * PI) * cameraDist;
			double yd = -Mth::sin(xRot / 180 * PI) * cameraDist;

			for (int i = 0; i < 8; i++)
			{
				float xo = (float)((i & 1) * 2 - 1);
				float yo = (float)(((i >> 1) & 1) * 2 - 1);
				float zo = (float)(((i >> 2) & 1) * 2 - 1);

				xo *= 0.1f;
				yo *= 0.1f;
				zo *= 0.1f;

				// 4J - corrected bug here where zo was also added to x component
				HitResult *hr = mc->level->clip(Vec3::newTemp(x + xo, y + yo, z + zo), Vec3::newTemp(x - xd + xo, y - yd + yo, z - zd + zo));
				if (hr != NULL)
				{
					double dist = hr->pos->distanceTo(Vec3::newTemp(x, y, z));
					if (dist < cameraDist) cameraDist = dist;
					delete hr;
				}
			}

			// 4J - removed extra rotations here that aren't needed because our xRot/yRot don't ever
			// deviate from the player's view direction
//			glRotatef(player->xRot - xRot, 1, 0, 0);
//			glRotatef(player->yRot - yRot, 0, 1, 0);
			glTranslatef(0, 0, (float) -cameraDist);
//			glRotatef(yRot - player->yRot, 0, 1, 0);
//			glRotatef(xRot - player->xRot, 1, 0, 0);
		}
	}
	else
	{
		glTranslatef(0, 0, -0.1f);
	}

	if (!mc->options->fixedCamera)
	{
		glRotatef(player->xRotO + (player->xRot - player->xRotO) * a, 1, 0, 0);
		if( localplayer->ThirdPersonView() == 2 )
		{
			// Third person view is now 0 for disabled, 1 for original, 2 for flipped
			glRotatef(player->yRotO + (player->yRot - player->yRotO) * a, 0, 1, 0);
		}
		else
		{
			glRotatef(player->yRotO + (player->yRot - player->yRotO) * a + 180, 0, 1, 0);
		}
	}

	glTranslatef(0, heightOffset, 0);

	x = player->xo + (player->x - player->xo) * a;
	y = player->yo + (player->y - player->yo) * a - heightOffset;
	z = player->zo + (player->z - player->zo) * a;

	isInClouds = mc->levelRenderer->isInCloud(x, y, z, a);

}


void GameRenderer::zoomRegion(double zoom, double xa, double ya)
{
	this->zoom = zoom;
	this->zoom_x = xa;
	this->zoom_y = ya;
}

void GameRenderer::unZoomRegion()
{
	zoom = 1;
}

// 4J added as we have more complex adjustments to make for fov & aspect on account of viewports
void GameRenderer::getFovAndAspect(float& fov, float& aspect, float a, bool applyEffects)
{
	// 4J - split out aspect ratio and fov here so we can adjust for viewports - we might need to revisit these as
	// they are maybe be too generous for performance. 
	aspect = mc->width / (float) mc->height;
	fov = getFov(a, applyEffects);

	if( ( mc->player->m_iScreenSection == C4JRender::VIEWPORT_TYPE_SPLIT_TOP ) ||
		( mc->player->m_iScreenSection == C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM ) )
	{
		aspect *= 2.0f;
		fov *= 0.7f;		// Reduce FOV to make things less fish-eye, at the expense of reducing vertical FOV from single player mode
	}
	else if( ( mc->player->m_iScreenSection == C4JRender::VIEWPORT_TYPE_SPLIT_LEFT ) ||
		( mc->player->m_iScreenSection == C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT) )
	{
		// Ideally I'd like to make the fov bigger here, but if I do then you an see that the arm isn't very long...
		aspect *= 0.5f;
	}
}

void GameRenderer::setupCamera(float a, int eye)
{
	renderDistance = (float)(16 * 16 >> (mc->options->viewDistance));
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	float stereoScale = 0.07f;
	if (mc->options->anaglyph3d) glTranslatef(-(eye * 2 - 1) * stereoScale, 0, 0);

	// 4J - have split out fov & aspect calculation so we can take into account viewports
	float aspect, fov;
	getFovAndAspect(fov, aspect, a, true);

	if (zoom != 1)
	{
		glTranslatef((float) zoom_x, (float) -zoom_y, 0);
		glScaled(zoom, zoom, 1);
	}
	gluPerspective(fov, aspect, 0.05f, renderDistance * 2);

    if (mc->gameMode->isCutScene())
	{
        float s = 1 / 1.5f;
        glScalef(1, s, 1);
    }

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	if (mc->options->anaglyph3d) glTranslatef((eye * 2 - 1) * 0.10f, 0, 0);

	bobHurt(a);

	// 4J-PB - this is a per-player option
	//if (mc->options->bobView) bobView(a);
	
	bool bNoLegAnim =(mc->player->getAnimOverrideBitmask()&(1<<HumanoidModel::eAnim_NoLegAnim))!=0;
	bool bNoBobbingAnim =(mc->player->getAnimOverrideBitmask()&(1<<HumanoidModel::eAnim_NoBobbing))!=0;

	if(app.GetGameSettings(mc->player->GetXboxPad(),eGameSetting_ViewBob) && !mc->player->abilities.flying && !bNoLegAnim && !bNoBobbingAnim) bobView(a);

	float pt = mc->player->oPortalTime + (mc->player->portalTime - mc->player->oPortalTime) * a;
	if (pt > 0)
	{
        int multiplier = 20;
        if (mc->player->hasEffect(MobEffect::confusion))
		{
            multiplier = 7;
        }

		float skew = 5 / (pt * pt + 5) - pt * 0.04f;
		skew *= skew;
		glRotatef((_tick + a) * multiplier, 0, 1, 1);
		glScalef(1 / skew, 1, 1);
		glRotatef(-(_tick + a) * multiplier, 0, 1, 1);
	}


	moveCameraToPlayer(a);

    if (cameraFlip > 0)
	{
        int i = cameraFlip - 1;
        if (i == 1) glRotatef(90, 0, 1, 0);
        if (i == 2) glRotatef(180, 0, 1, 0);
        if (i == 3) glRotatef(-90, 0, 1, 0);
        if (i == 4) glRotatef(90, 1, 0, 0);
        if (i == 5) glRotatef(-90, 1, 0, 0);
    }
}

void GameRenderer::renderItemInHand(float a, int eye)
{
	if (cameraFlip > 0) return;

	shared_ptr<LocalPlayer> localplayer = dynamic_pointer_cast<LocalPlayer>(mc->cameraTargetPlayer);

	// 4J-PB - to turn off the hand for screenshots, but not when the item held is a map
	if ( localplayer!=NULL)
	{
		shared_ptr<ItemInstance> item = localplayer->inventory->getSelected();
		if(!(item && item->getItem()->id==Item::map_Id)  && app.GetGameSettings(localplayer->GetXboxPad(),eGameSetting_DisplayHand)==0 ) return;
	}

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();


    float stereoScale = 0.07f;
    if (mc->options->anaglyph3d) glTranslatef(-(eye * 2 - 1) * stereoScale, 0, 0);

	// 4J - have split out fov & aspect calculation so we can take into account viewports
	float fov, aspect;
	getFovAndAspect(fov, aspect, a, false);

    if (zoom != 1)
	{
        glTranslatef((float) zoom_x, (float) -zoom_y, 0);
        glScaled(zoom, zoom, 1);
    }
    gluPerspective(fov, aspect, 0.05f, renderDistance * 2);

    if (mc->gameMode->isCutScene())
	{
        float s = 1 / 1.5f;
        glScalef(1, s, 1);
    }

    glMatrixMode(GL_MODELVIEW);


    glLoadIdentity();
	if (mc->options->anaglyph3d) glTranslatef((eye * 2 - 1) * 0.10f, 0, 0);

	glPushMatrix();
	bobHurt(a);

	// 4J-PB - changing this to be per player
	//if (mc->options->bobView) bobView(a);
	bool bNoLegAnim =(localplayer->getAnimOverrideBitmask()&( (1<<HumanoidModel::eAnim_NoLegAnim) | (1<<HumanoidModel::eAnim_NoBobbing) ))!=0;
	if(app.GetGameSettings(localplayer->GetXboxPad(),eGameSetting_ViewBob) && !localplayer->abilities.flying && !bNoLegAnim) bobView(a);

	// 4J-PB - changing this to be per player
	//if (!mc->options->thirdPersonView && !mc->cameraTargetPlayer->isSleeping())
	if (!localplayer->ThirdPersonView() && !mc->cameraTargetPlayer->isSleeping())
	{
		if (!mc->options->hideGui && !mc->gameMode->isCutScene()) 
		{
			turnOnLightLayer(a);
			PIXBeginNamedEvent(0,"Item in hand render");
			itemInHandRenderer->render(a);
			PIXEndNamedEvent();
			turnOffLightLayer(a);
		}
	}
	glPopMatrix();
	// 4J-PB - changing this to be per player
	//if (!mc->options->thirdPersonView && !mc->cameraTargetPlayer->isSleeping())
	if (!localplayer->ThirdPersonView() && !mc->cameraTargetPlayer->isSleeping())
	{
		itemInHandRenderer->renderScreenEffect(a);
		bobHurt(a);
	}
	// 4J-PB - changing this to be per player
	//if (mc->options->bobView) bobView(a);
	if(app.GetGameSettings(localplayer->GetXboxPad(),eGameSetting_ViewBob) && !localplayer->abilities.flying && !bNoLegAnim) bobView(a);
}

// 4J - change brought forward from 1.8.2
void GameRenderer::turnOffLightLayer(double alpha)
{	// 4J - TODO
#if 0	
    if (SharedConstants::TEXTURE_LIGHTING)
	{
        glClientActiveTexture(GL_TEXTURE1);
        glActiveTexture(GL_TEXTURE1);
        glDisable(GL_TEXTURE_2D);
        glClientActiveTexture(GL_TEXTURE0);
        glActiveTexture(GL_TEXTURE0);
    }
#endif
	RenderManager.TextureBindVertex(-1);
}

// 4J - change brought forward from 1.8.2
void GameRenderer::turnOnLightLayer(double alpha)
{	// 4J - TODO
#if 0
    if (SharedConstants::TEXTURE_LIGHTING)
	{
        glClientActiveTexture(GL_TEXTURE1);
        glActiveTexture(GL_TEXTURE1);
        glMatrixMode(GL_TEXTURE);
        glLoadIdentity();
// float s = 1 / 16f / 15.0f*16/14.0f;
        float s = 1 / 16.0f / 15.0f * 15 / 16;
        glScalef(s, s, s);
        glTranslatef(8f, 8f, 8f);
        glMatrixMode(GL_MODELVIEW);

        mc->textures->bind(lightTexture);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
        glColor4f(1, 1, 1, 1);
        glEnable(GL_TEXTURE_2D);
        glClientActiveTexture(GL_TEXTURE0);
        glActiveTexture(GL_TEXTURE0);
    }
#endif
	RenderManager.TextureBindVertex(getLightTexture(mc->player->GetXboxPad(), mc->level));
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
}

// 4J - change brought forward from 1.8.2
void GameRenderer::tickLightTexture()
{
    blrt += (float)((Math::random() - Math::random()) * Math::random() * Math::random());
    blgt += (float)((Math::random() - Math::random()) * Math::random() * Math::random());
    blrt *= 0.9;
    blgt *= 0.9;
    blr += (blrt - blr) * 1;
    blg += (blgt - blg) * 1;
    _updateLightTexture = true;
}

void GameRenderer::updateLightTexture(float a)
{
	// 4J-JEV: Now doing light textures on PER PLAYER basis.
	// 4J - we *had* added separate light textures for all dimensions, and this loop to update them all here
	for(int j = 0; j < XUSER_MAX_COUNT; j++ )
	{
		// Loop over all the players
		shared_ptr<MultiplayerLocalPlayer> player = Minecraft::GetInstance()->localplayers[j];
		if (player == NULL) continue;

		Level *level = player->level;		// 4J - was mc->level when it was just to update the one light texture
		
		float skyDarken1 = level->getSkyDarken((float) 1);
		for (int i = 0; i < 256; i++)
		{
			float darken = skyDarken1 * 0.95f + 0.05f;
			float sky = level->dimension->brightnessRamp[i / 16] * darken;
			float block = level->dimension->brightnessRamp[i % 16] * (blr * 0.1f + 1.5f);

			if (level->lightningBoltTime > 0)
			{
				sky = level->dimension->brightnessRamp[i / 16];
			}

			float rs = sky * (skyDarken1 * 0.65f + 0.35f);
			float gs = sky * (skyDarken1 * 0.65f + 0.35f);
			float bs = sky;

			float rb = block;
			float gb = block * ((block * 0.6f + 0.4f) * 0.6f + 0.4f);
			float bb = block * ((block * block) * 0.6f + 0.4f);

			float _r = (rs + rb);
			float _g = (gs + gb);
			float _b = (bs + bb);

			_r = _r * 0.96f + 0.03f;
			_g = _g * 0.96f + 0.03f;
			_b = _b * 0.96f + 0.03f;

			if (level->dimension->id == 1)
			{
				_r = (0.22f + rb * 0.75f);
				_g = (0.28f + gb * 0.75f);
				_b = (0.25f + bb * 0.75f);
			}

			if (player->hasEffect(MobEffect::nightVision))
			{
				float scale = getNightVisionScale(player, a);
				{
					float dist = 1.0f / _r;
					if (dist > (1.0f / _g))
					{
						dist = (1.0f / _g);
					}
					if (dist > (1.0f / _b))
					{
						dist = (1.0f / _b);
					}
					_r = _r * (1.0f - scale) + (_r * dist) * scale;
					_g = _g * (1.0f - scale) + (_g * dist) * scale;
					_b = _b * (1.0f - scale) + (_b * dist) * scale;
				}
			}

			float brightness = 0.0f;	// 4J - TODO - was mc->options->gamma;
			if (_r > 1) _r = 1;
			if (_g > 1) _g = 1;
			if (_b > 1) _b = 1;

			float ir = 1 - _r;
			float ig = 1 - _g;
			float ib = 1 - _b;
			ir = 1 - (ir * ir * ir * ir);
			ig = 1 - (ig * ig * ig * ig);
			ib = 1 - (ib * ib * ib * ib);
			_r = _r * (1 - brightness) + ir * brightness;
			_g = _g * (1 - brightness) + ig * brightness;
			_b = _b * (1 - brightness) + ib * brightness;


			_r = _r * 0.96f + 0.03f;
			_g = _g * 0.96f + 0.03f;
			_b = _b * 0.96f + 0.03f;


			if (_r > 1) _r = 1;
			if (_g > 1) _g = 1;
			if (_b > 1) _b = 1;
			if (_r < 0) _r = 0;
			if (_g < 0) _g = 0;
			if (_b < 0) _b = 0;

			int a = 255;
			int r = (int) (_r * 255);
			int g = (int) (_g * 255);
			int b = (int) (_b * 255);

#if ( defined _DURANGO || defined _WIN64 || __PSVITA__ )
			lightPixels[j][i] = a << 24 | b << 16 | g << 8 | r;
#elif ( defined _XBOX || defined __ORBIS__ )
			lightPixels[j][i] = a << 24 | r << 16 | g << 8 | b;
#else
			lightPixels[j][i] = r << 24 | g << 16 | b << 8 | a;
#endif
		}

		mc->textures->replaceTextureDirect( lightPixels[j], 16, 16, getLightTexture(j,level) );
	}
}

float GameRenderer::getNightVisionScale(shared_ptr<Player> player, float a)
{
	int duration = player->getEffect(MobEffect::nightVision)->getDuration();
	if (duration > (SharedConstants::TICKS_PER_SECOND * 10))
	{
		return 1.0f;
	}
	else
	{
		float flash = max(0.0f, (float)duration - a);
		return .7f + Mth::sin(flash * PI * .05f) * .3f; // was:  .7 + sin(flash*pi*0.2) * .3
	}
}

// 4J added, so we can have a light texture for each player to support split screen
int GameRenderer::getLightTexture(int iPad, Level *level)
{
	// Turn the current dimenions id into an index from 0 to 2
	// int idx = level->dimension->id;
	// if( idx == -1 ) idx = 2;

	return lightTexture[iPad]; // 4J-JEV: Changing to Per Player lighting textures.
}

void GameRenderer::render(float a, bool bFirst)
{
	if( _updateLightTexture && bFirst) updateLightTexture(a);
	if (Display::isActive())
	{
		lastActiveTime = System::currentTimeMillis();
	}
	else
	{
		if (System::currentTimeMillis() - lastActiveTime > 500)
		{
			mc->pauseGame();
		}
	}

#if 0	// 4J - TODO
	if (mc->mouseGrabbed) {
		mc->mouseHandler.poll();

		float ss = mc->options->sensitivity * 0.6f + 0.2f;
		float sens = (ss * ss * ss) * 8;
		float xo = mc->mouseHandler.xd * sens;
		float yo = mc->mouseHandler.yd * sens;

		int yAxis = 1;
		if (mc->options->invertYMouse) yAxis = -1;

		if (Minecraft.DEADMAU5_CAMERA_CHEATS) {
			if (!mc->options->fixedCamera) {
				if (Keyboard.isKeyDown(Keyboard.KEY_J)) {
					xo = -12f * mc->options->sensitivity;
				} else if (Keyboard.isKeyDown(Keyboard.KEY_L)) {
					xo = 12f * mc->options->sensitivity;
				}
				if (Keyboard.isKeyDown(Keyboard.KEY_I)) {
					yo = 12f * mc->options->sensitivity;
				} else if (Keyboard.isKeyDown(Keyboard.KEY_K)) {
					yo = -12f * mc->options->sensitivity;
				}
			}
		}

		if (mc->options->smoothCamera) {

			xo = smoothTurnX.getNewDeltaValue(xo, .05f * sens);
			yo = smoothTurnY.getNewDeltaValue(yo, .05f * sens);

		}

		mc->player.turn(xo, yo * yAxis);
	}
#endif

	if (mc->noRender) return;
	GameRenderer::anaglyph3d = mc->options->anaglyph3d;

	glViewport(0, 0, mc->width, mc->height);	// 4J - added
	ScreenSizeCalculator ssc(mc->options, mc->width, mc->height);
	int screenWidth = ssc.getWidth();
	int screenHeight = ssc.getHeight();
	int xMouse = Mouse::getX() * screenWidth / mc->width;
	int yMouse = screenHeight - Mouse::getY() * screenHeight / mc->height - 1;

	int maxFps = getFpsCap(mc->options->framerateLimit);

	if (mc->level != NULL)
	{
		if (mc->options->framerateLimit == 0)
		{
			renderLevel(a, 0);
		}
		else
		{
			renderLevel(a, lastNsTime + 1000000000 / maxFps);
		}

		lastNsTime = System::nanoTime();


		if (!mc->options->hideGui || mc->screen != NULL)
		{
			mc->gui->render(a, mc->screen != NULL, xMouse, yMouse);
		}
	}
	else
	{
		glViewport(0, 0, mc->width, mc->height);
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		setupGuiScreen();

		lastNsTime = System::nanoTime();
	}


	if (mc->screen != NULL)
	{
		glClear(GL_DEPTH_BUFFER_BIT);
		mc->screen->render(xMouse, yMouse, a);
		if (mc->screen != NULL && mc->screen->particles != NULL) mc->screen->particles->render(a);
	}

}

void GameRenderer::renderLevel(float a)
{
	renderLevel(a, 0);
}

#ifdef MULTITHREAD_ENABLE
// Request that an item be deleted, when it is safe to do so
void GameRenderer::AddForDelete(byte *deleteThis)
{
	EnterCriticalSection(&m_csDeleteStack);
	m_deleteStackByte.push_back(deleteThis);
}

void GameRenderer::AddForDelete(SparseLightStorage *deleteThis)
{
	EnterCriticalSection(&m_csDeleteStack);
	m_deleteStackSparseLightStorage.push_back(deleteThis);
}

void GameRenderer::AddForDelete(CompressedTileStorage *deleteThis)
{
	EnterCriticalSection(&m_csDeleteStack);
	m_deleteStackCompressedTileStorage.push_back(deleteThis);
}

void GameRenderer::AddForDelete(SparseDataStorage *deleteThis)
{
	EnterCriticalSection(&m_csDeleteStack);
	m_deleteStackSparseDataStorage.push_back(deleteThis);
}

void GameRenderer::FinishedReassigning()
{
	LeaveCriticalSection(&m_csDeleteStack);
}

int GameRenderer::runUpdate(LPVOID lpParam)
{
	Minecraft *minecraft = Minecraft::GetInstance();
	Vec3::CreateNewThreadStorage();
	AABB::CreateNewThreadStorage();
	IntCache::CreateNewThreadStorage();
	Tesselator::CreateNewThreadStorage(1024*1024);	
	Compression::UseDefaultThreadStorage();
	RenderManager.InitialiseContext();
#ifdef _LARGE_WORLDS
	Chunk::CreateNewThreadStorage();
#endif
	Tile::CreateNewThreadStorage();

	ShutdownManager::HasStarted(ShutdownManager::eRenderChunkUpdateThread,m_updateEvents);
	while(ShutdownManager::ShouldRun(ShutdownManager::eRenderChunkUpdateThread))
	{
		//m_updateEvents->Clear(eUpdateEventIsFinished);
		//m_updateEvents->WaitForSingle(eUpdateCanRun,INFINITE);
		// 4J Stu - We Need to have this happen atomically to avoid deadlocks
		m_updateEvents->WaitForAll(INFINITE);

		if( !ShutdownManager::ShouldRun(ShutdownManager::eRenderChunkUpdateThread) )
		{
			break;
		}

		m_updateEvents->Set(eUpdateCanRun);

//		PIXBeginNamedEvent(0,"Updating dirty chunks %d",(count++)&7);

		// Update chunks atomically until there aren't any very near ones left - they will be deferred for rendering
		// until the call to CBuffDeferredModeEnd if we have anything near to render here
		// Now limiting maximum number of updates that can be deferred as have noticed that with redstone clock circuits, it is possible to create
		// things that need constant updating, so if you stand near them, the render data Never gets updated and the game just keeps going until it runs out of render memory...
		int count = 0;
		static const int MAX_DEFERRED_UPDATES = 10;
		bool shouldContinue = false;
		do
		{
			shouldContinue = minecraft->levelRenderer->updateDirtyChunks();
			count++;
		} while ( shouldContinue && count < MAX_DEFERRED_UPDATES );

//		while( minecraft->levelRenderer->updateDirtyChunks() )
//			;
		RenderManager.CBuffDeferredModeEnd();

		// If any renderable tile entities were flagged in this last block of chunk(s) that were udpated, then change their
		// flags to say that this deferred chunk is over and they are actually safe to be removed now
		minecraft->levelRenderer->fullyFlagRenderableTileEntitiesToBeRemoved();

		// We've got stacks for things that can only safely be deleted whilst this thread isn't updating things - delete those things now
		EnterCriticalSection(&m_csDeleteStack);
		for(unsigned int i = 0; i < m_deleteStackByte.size(); i++ )
		{
			delete m_deleteStackByte[i];
		}
		m_deleteStackByte.clear();
		for(unsigned int i = 0; i < m_deleteStackSparseLightStorage.size(); i++ )
		{
			delete m_deleteStackSparseLightStorage[i];
		}
		m_deleteStackSparseLightStorage.clear();
		for(unsigned int i = 0; i < m_deleteStackCompressedTileStorage.size(); i++ )
		{
			delete m_deleteStackCompressedTileStorage[i];
		}
		m_deleteStackCompressedTileStorage.clear();
		for(unsigned int i = 0; i < m_deleteStackSparseDataStorage.size(); i++ )
		{
			delete m_deleteStackSparseDataStorage[i];
		}
		m_deleteStackSparseDataStorage.clear();
		LeaveCriticalSection(&m_csDeleteStack);
			
//		PIXEndNamedEvent();

		AABB::resetPool();
		Vec3::resetPool();
		IntCache::Reset();	
		m_updateEvents->Set(eUpdateEventIsFinished);
	}

	ShutdownManager::HasFinished(ShutdownManager::eRenderChunkUpdateThread);
	return 0;
}
#endif

void GameRenderer::EnableUpdateThread()
{
// #ifdef __PS3__ // MGH - disable the update on PS3 for now
// 	return;
// #endif
#ifdef MULTITHREAD_ENABLE
	if( updateRunning) return;
	app.DebugPrintf("------------------EnableUpdateThread--------------------\n");
	updateRunning = true;
	m_updateEvents->Set(eUpdateCanRun);
	m_updateEvents->Set(eUpdateEventIsFinished);
#endif
}

void GameRenderer::DisableUpdateThread()
{
// #ifdef __PS3__ // MGH - disable the update on PS3 for now
// 	return;
// #endif
#ifdef MULTITHREAD_ENABLE
	if( !updateRunning) return;
	app.DebugPrintf("------------------DisableUpdateThread--------------------\n");
	updateRunning = false;
	m_updateEvents->Clear(eUpdateCanRun);	
	m_updateEvents->WaitForSingle(eUpdateEventIsFinished,INFINITE);
#endif
}

void GameRenderer::renderLevel(float a, __int64 until)
{
//	if (updateLightTexture) updateLightTexture();	// 4J - TODO - Java 1.0.1 has this line enabled, should check why - don't want to put it in now in case it breaks split-screen

	glEnable(GL_CULL_FACE);
	glEnable(GL_DEPTH_TEST);

	// Is this the primary player? Only do the updating of chunks if it is. This controls the creation of render data for each chunk - all of this we are only
	// going to do for the primary player, and the other players can just view whatever they have loaded in - we're sharing render data between players.
	bool updateChunks = ( mc->player == mc->localplayers[ProfileManager.GetPrimaryPad()] );

//	if (mc->cameraTargetPlayer == NULL)	// 4J - removed condition as we want to update this is mc->player changes for different local players
	{
		mc->cameraTargetPlayer = mc->player;
	}
	pick(a);

	shared_ptr<Mob> cameraEntity = mc->cameraTargetPlayer;
	LevelRenderer *levelRenderer = mc->levelRenderer;
	ParticleEngine *particleEngine = mc->particleEngine;
	double xOff = cameraEntity->xOld + (cameraEntity->x - cameraEntity->xOld) * a;
	double yOff = cameraEntity->yOld + (cameraEntity->y - cameraEntity->yOld) * a;
	double zOff = cameraEntity->zOld + (cameraEntity->z - cameraEntity->zOld) * a;

	for (int i = 0; i < 2; i++)
	{
		if (mc->options->anaglyph3d)
		{
			GameRenderer::anaglyphPass = i;
			if (GameRenderer::anaglyphPass == 0) glColorMask(false, true, true, false);
			else glColorMask(true, false, false, false);
		}


		glViewport(0, 0, mc->width, mc->height);
		setupClearColor(a);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glEnable(GL_CULL_FACE);

		setupCamera(a, i);
		Camera::prepare(mc->player, mc->player->ThirdPersonView() == 2);

		Frustum::getFrustum();
		if (mc->options->viewDistance < 2)
		{
			setupFog(-1, a);
			levelRenderer->renderSky(a);
			if(mc->skins->getSelected()->getId() == 1026 ) levelRenderer->renderHaloRing(a);
		}
		glEnable(GL_FOG);
		setupFog(1, a);

		if (mc->options->ambientOcclusion)
		{
			GL11::glShadeModel(GL11::GL_SMOOTH);
		}

		PIXBeginNamedEvent(0,"Culling");
		MemSect(31);
//		Culler *frustum = new FrustumCuller();
		FrustumCuller frustObj;
		Culler *frustum = &frustObj;
		MemSect(0);
		frustum->prepare(xOff, yOff, zOff);

		mc->levelRenderer->cull(frustum, a);
		PIXEndNamedEvent();

#ifndef MULTITHREAD_ENABLE
		if ( (i == 0) && updateChunks ) // 4J - added updateChunks condition
		{
			int PIXPass = 0;
			PIXBeginNamedEvent(0,"Updating dirty chunks");
			do
			{
				PIXBeginNamedEvent(0,"Updating dirty chunks pass %d",PIXPass++);
				bool retval = mc->levelRenderer->updateDirtyChunks(cameraEntity, false);
				PIXEndNamedEvent();
				if( retval ) break;


				if (until == 0) break;

				__int64 diff = until - System::nanoTime();
				if (diff < 0) break;
				if (diff > 1000000000) break;
			} while (true);
			PIXEndNamedEvent();
		}
#endif
		setupFog(0, a);
		glEnable(GL_FOG);
		MemSect(31);
		mc->textures->bindTexture(TN_TERRAIN);	// 4J was L"/terrain.png"
		MemSect(0);
		Lighting::turnOff();
		PIXBeginNamedEvent(0,"Level render");
 		levelRenderer->render(cameraEntity, 0, a, updateChunks);
		PIXEndNamedEvent();

		GL11::glShadeModel(GL11::GL_FLAT);

		if (cameraFlip == 0 )
		{
			Lighting::turnOn();
			PIXBeginNamedEvent(0,"Entity render");
			// 4J - for entities, don't include the "a" factor that interpolates from the old to new position, as the AABBs for the entities are already fully at the new position
			// This fixes flickering minecarts, and pigs that you are riding on
			frustum->prepare(cameraEntity->x,cameraEntity->y,cameraEntity->z);
			// 4J Stu - When rendering entities, in the end if the dragon is hurt or we have a lot of entities we can end up wrapping
			// our index into the temp Vec3 cache and overwrite the one that was storing the camera position
			// Fix for #77745 - TU9: Content: Gameplay: Items and mobs not belonging to end world are disappearing when Enderdragon is damaged.
			Vec3 *cameraPosTemp = cameraEntity->getPos(a);
			cameraPos->x = cameraPosTemp->x;
			cameraPos->y = cameraPosTemp->y;
			cameraPos->z = cameraPosTemp->z;
			levelRenderer->renderEntities(cameraPos, frustum, a);
#ifdef __PSVITA__
			// AP - make sure we're using the Alpha cut out effect for particles
			glEnable(GL_ALPHA_TEST);
#endif
			PIXEndNamedEvent();
			PIXBeginNamedEvent(0,"Particle render");
			turnOnLightLayer(a);		// 4J - brought forward from 1.8.2
			particleEngine->renderLit(cameraEntity, a);
			Lighting::turnOff();
			setupFog(0, a);
			particleEngine->render(cameraEntity, a);
			PIXEndNamedEvent();
			turnOffLightLayer(a);		// 4J - brought forward from 1.8.2

			shared_ptr<Player> player = dynamic_pointer_cast<Player>(cameraEntity);
			if (mc->hitResult != NULL && cameraEntity->isUnderLiquid(Material::water) && player!=NULL) //&& !mc->options.hideGui)
			{
				//shared_ptr<Player> player = dynamic_pointer_cast<Player>(cameraEntity);
				glDisable(GL_ALPHA_TEST);
				levelRenderer->renderHit(player, mc->hitResult, 0, player->inventory->getSelected(), a);
				levelRenderer->renderHitOutline(player, mc->hitResult, 0, player->inventory->getSelected(), a);
				glEnable(GL_ALPHA_TEST);
			}
		}

        glDisable(GL_BLEND);
        glEnable(GL_CULL_FACE);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glDepthMask(true);
		setupFog(0, a);
		glEnable(GL_BLEND);
		glDisable(GL_CULL_FACE);
		MemSect(31);
		mc->textures->bindTexture(TN_TERRAIN);		// 4J was L"/terrain.png"
		MemSect(0);
		// 4J - have changed this fancy rendering option to work with our command buffers. The original used to use frame buffer flags to disable
		// writing to colour when doing the z-only pass, but that value gets obliterated by our command buffers. Using alpha blend function instead
		// to achieve the same effect.
		if (true)	// (mc->options->fancyGraphics)
		{
			if (mc->options->ambientOcclusion)
			{
				GL11::glShadeModel(GL11::GL_SMOOTH);
			}

			glBlendFunc(GL_ZERO, GL_ONE);
			PIXBeginNamedEvent(0,"Fancy second pass - writing z");
			int visibleWaterChunks = levelRenderer->render(cameraEntity, 1, a, updateChunks);
			PIXEndNamedEvent();
			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

			if (visibleWaterChunks > 0)
			{
				PIXBeginNamedEvent(0,"Fancy second pass - actual rendering");
				levelRenderer->render(cameraEntity, 1, a, updateChunks);	// 4J - chanaged, used to be renderSameAsLast but we don't support that anymore
				PIXEndNamedEvent();
			}

			GL11::glShadeModel(GL11::GL_FLAT);
		}
		else
		{
			PIXBeginNamedEvent(0,"Second pass level render");
			levelRenderer->render(cameraEntity, 1, a, updateChunks);
			PIXEndNamedEvent();
		}


		glDepthMask(true);
		glEnable(GL_CULL_FACE);
		glDisable(GL_BLEND);

		if (zoom == 1 && (dynamic_pointer_cast<Player>(cameraEntity)!=NULL)) //&& !mc->options.hideGui)
		{
			if (mc->hitResult != NULL && !cameraEntity->isUnderLiquid(Material::water))
			{
				shared_ptr<Player> player = dynamic_pointer_cast<Player>(cameraEntity);
				glDisable(GL_ALPHA_TEST);
				levelRenderer->renderHit(player, mc->hitResult, 0, player->inventory->getSelected(), a);
				levelRenderer->renderHitOutline(player, mc->hitResult, 0, player->inventory->getSelected(), a);
				glEnable(GL_ALPHA_TEST);
			}
		}

		/* 4J - moved rain rendering to after clouds so that it alpha blends onto them properly
		PIXBeginNamedEvent(0,"Rendering snow and rain");
		renderSnowAndRain(a);
		PIXEndNamedEvent();
		glDisable(GL_FOG);
		*/

		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE);
		levelRenderer->renderDestroyAnimation(Tesselator::getInstance(), dynamic_pointer_cast<Player>(cameraEntity), a);
		glDisable(GL_BLEND);

        if (mc->options->isCloudsOn())
		{
			glPushMatrix();
			setupFog(0, a);
			glEnable(GL_FOG);
			PIXBeginNamedEvent(0,"Rendering clouds");
			levelRenderer->renderClouds(a);
			PIXEndNamedEvent();
			glDisable(GL_FOG);
			setupFog(1, a);
			glPopMatrix();
		}

		// 4J - rain rendering moved here so that it renders after clouds & can blend properly onto them
		setupFog(0, a);
		glEnable(GL_FOG);
		PIXBeginNamedEvent(0,"Rendering snow and rain");
		renderSnowAndRain(a);
		PIXEndNamedEvent();
		glDisable(GL_FOG);


		if (zoom == 1)
		{
			glClear(GL_DEPTH_BUFFER_BIT);
			renderItemInHand(a, i);
		}


		if (!mc->options->anaglyph3d)
		{
			return;
		}
	}
	glColorMask(true, true, true, false);
}

void GameRenderer::tickRain()
{
	float rainLevel = mc->level->getRainLevel(1);

	if (!mc->options->fancyGraphics) rainLevel /= 2;
	if (rainLevel == 0) return;

	rainLevel /= ( mc->levelRenderer->activePlayers() + 1 );

	random->setSeed(_tick * 312987231l);
	shared_ptr<Mob> player = mc->cameraTargetPlayer;
	Level *level = mc->level;

	int x0 = Mth::floor(player->x);
	int y0 = Mth::floor(player->y);
	int z0 = Mth::floor(player->z);

	int r = 10;

	double rainPosX = 0;
	double rainPosY = 0;
	double rainPosZ = 0;
	int rainPosSamples = 0;

    int rainCount = (int) (100 * rainLevel * rainLevel);
    if (mc->options->particles == 1)
	{
        rainCount >>= 1;
    } else if (mc->options->particles == 2)
	{
        rainCount = 0;
    }
    for (int i = 0; i < rainCount; i++)
	{
		int x = x0 + random->nextInt(r) - random->nextInt(r);
		int z = z0 + random->nextInt(r) - random->nextInt(r);
		int y = level->getTopRainBlock(x, z);
		int t = level->getTile(x, y - 1, z);
		Biome *biome = level->getBiome(x,z);
		if (y <= y0 + r && y >= y0 - r && biome->hasRain() && biome->getTemperature() >= 0.2f)
		{
			float xa = random->nextFloat();
			float za = random->nextFloat();
			if (t > 0)
			{
				if (Tile::tiles[t]->material == Material::lava)
				{
					mc->particleEngine->add( shared_ptr<SmokeParticle>( new SmokeParticle(level, x + xa, y + 0.1f - Tile::tiles[t]->getShapeY0(), z + za, 0, 0, 0) ) );
				}
				else
				{
					if (random->nextInt(++rainPosSamples) == 0)
					{
						rainPosX = x + xa;
						rainPosY = y + 0.1f - Tile::tiles[t]->getShapeY0();
						rainPosZ = z + za;
					}
					mc->particleEngine->add( shared_ptr<WaterDropParticle>( new WaterDropParticle(level, x + xa, y + 0.1f - Tile::tiles[t]->getShapeY0(), z + za) ) );
				}
			}
		}
	}


	if (rainPosSamples > 0 && random->nextInt(3) < rainSoundTime++)
	{
		rainSoundTime = 0;
		MemSect(24);
		if (rainPosY > player->y + 1 && level->getTopRainBlock(Mth::floor(player->x), Mth::floor(player->z)) > Mth::floor(player->y))
		{
			mc->level->playLocalSound(rainPosX, rainPosY, rainPosZ, eSoundType_AMBIENT_WEATHER_RAIN, 0.1f, 0.5f);
		}
		else
		{
			mc->level->playLocalSound(rainPosX, rainPosY, rainPosZ, eSoundType_AMBIENT_WEATHER_RAIN, 0.2f, 1.0f);
		}
		MemSect(0);
	}

}

// 4J - this whole function updated from 1.8.2
void GameRenderer::renderSnowAndRain(float a)
{
	float rainLevel = mc->level->getRainLevel(a);
	if (rainLevel <= 0) return;

	// 4J - rain is relatively low poly, but high fill-rate - better to clip it
	RenderManager.StateSetEnableViewportClipPlanes(true);

	this->turnOnLightLayer(a);

    if (rainXa == NULL) 
	{
        rainXa = new float[32 * 32];
        rainZa = new float[32 * 32];

        for (int z = 0; z < 32; z++)
		{
            for (int x = 0; x < 32; x++)
			{
                float xa = x - 16;
                float za = z - 16;
                float d = Mth::sqrt(xa * xa + za * za);
                rainXa[z << 5 | x] = -za / d;
                rainZa[z << 5 | x] = xa / d;
            }
        }
    }

	shared_ptr<Mob> player = mc->cameraTargetPlayer;
	Level *level = mc->level;

	int x0 = Mth::floor(player->x);
	int y0 = Mth::floor(player->y);
	int z0 = Mth::floor(player->z);

	Tesselator *t = Tesselator::getInstance();
	glDisable(GL_CULL_FACE);
	glNormal3f(0, 1, 0);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glAlphaFunc(GL_GREATER, 0.01f);

	MemSect(31);
	mc->textures->bindTexture(TN_ENVIRONMENT_SNOW);	// 4J was L"/environment/snow.png"
	MemSect(0);

	double xo = player->xOld + (player->x - player->xOld) * a;
	double yo = player->yOld + (player->y - player->yOld) * a;
	double zo = player->zOld + (player->z - player->zOld) * a;

	int yMin = Mth::floor(yo);


	int r = 5;
	// 4J - was if(mc.options.fancyGraphics) r = 10;
	switch( mc->levelRenderer->activePlayers() )
	{
		case 1:
		default:
			r = 9;
			break;
		case 2:
			r = 7;
			break;
		case 3:
			r = 5;
			break;
		case 4:
			r = 5;
			break;
	}

	// 4J - some changes made here to access biome through new interface that caches results in levelchunk flags, as an optimisation
	
    int mode = -1;
    float time = _tick + a;

    glColor4f(1, 1, 1, 1);

	for (int x = x0 - r; x <= x0 + r; x++)
		for (int z = z0 - r; z <= z0 + r; z++)
		{
            int rainSlot = (z - z0 + 16) * 32 + (x - x0 + 16);
            float xa = rainXa[rainSlot] * 0.5f;
            float za = rainZa[rainSlot] * 0.5f;
			
			// 4J - changes here brought forward from 1.8.2
			Biome *b = level->getBiome(x, z);
			if (!b->hasRain() && !b->hasSnow()) continue;

			int floor = level->getTopRainBlock(x, z);

			int yy0 = y0 - r;
			int yy1 = y0 + r;

			if (yy0 < floor) yy0 = floor;
			if (yy1 < floor) yy1 = floor;
			float s = 1;

            int yl = floor;
            if (yl < yMin) yl = yMin;

			if (yy0 != yy1)
			{
				random->setSeed((x * x * 3121 + x * 45238971) ^ (z * z * 418711 + z * 13761));

				// 4J - changes here brought forward from 1.8.2
				float temp = b->getTemperature();
				if (level->getBiomeSource()->scaleTemp(temp, floor) >= 0.15f)
				{
                    if (mode != 0)
					{
                        if (mode >= 0) t->end();
                        mode = 0;
                        mc->textures->bindTexture(TN_ENVIRONMENT_RAIN);
                        t->begin();
                    }

					float ra = (((_tick + x * x * 3121 + x * 45238971 + z * z * 418711 + z * 13761) & 31) + a) / 32.0f * (3 + random->nextFloat());

					double xd = (x + 0.5f) - player->x;
					double zd = (z + 0.5f) - player->z;
					float dd = (float) Mth::sqrt(xd * xd + zd * zd) / r;

                    float br = 1;
                    t->offset(-xo * 1, -yo * 1, -zo * 1);
#ifdef __PSVITA__
					// AP - this will set up the 4 vertices in half the time
					float Alpha = ((1 - dd * dd) * 0.5f + 0.5f) * rainLevel;
					int tex2 = (level->getLightColor(x, yl, z, 0) * 3 + 0xf000f0) / 4;
					t->tileRainQuad(x - xa + 0.5, yy0, z - za + 0.5, 0 * s, yy0 * s / 4.0f + ra * s,
									x + xa + 0.5, yy0, z + za + 0.5, 1 * s, yy0 * s / 4.0f + ra * s, 
									x + xa + 0.5, yy1, z + za + 0.5, 1 * s, yy1 * s / 4.0f + ra * s, 
									x - xa + 0.5, yy1, z - za + 0.5, 0 * s, yy1 * s / 4.0f + ra * s, 
									br, br, br, Alpha, br, br, br, 0, tex2);
#else
                    t->tex2(level->getLightColor(x, yl, z, 0));
                    t->color(br, br, br, ((1 - dd * dd) * 0.5f + 0.5f) * rainLevel);
                    t->vertexUV(x - xa + 0.5, yy0, z - za + 0.5, 0 * s, yy0 * s / 4.0f + ra * s);
                    t->vertexUV(x + xa + 0.5, yy0, z + za + 0.5, 1 * s, yy0 * s / 4.0f + ra * s);
					t->color(br, br, br, 0.0f);														// 4J - added to soften the top visible edge of the rain
                    t->vertexUV(x + xa + 0.5, yy1, z + za + 0.5, 1 * s, yy1 * s / 4.0f + ra * s);
					t->vertexUV(x - xa + 0.5, yy1, z - za + 0.5, 0 * s, yy1 * s / 4.0f + ra * s);
#endif
					t->offset(0, 0, 0);
					t->end();
				}
				else
				{
                    if (mode != 1)
					{
                        if (mode >= 0) t->end();
                        mode = 1;
                        mc->textures->bindTexture(TN_ENVIRONMENT_SNOW);
                        t->begin();
                    }
                    float ra = (((_tick) & 511) + a) / 512.0f;
                    float uo = random->nextFloat() + time * 0.01f * (float) random->nextGaussian();
                    float vo = random->nextFloat() + time * (float) random->nextGaussian() * 0.001f;
                    double xd = (x + 0.5f) - player->x;
                    double zd = (z + 0.5f) - player->z;
                    float dd = (float) sqrt(xd * xd + zd * zd) / r;
                    float br = 1;
                    t->offset(-xo * 1, -yo * 1, -zo * 1);
#ifdef __PSVITA__
					// AP - this will set up the 4 vertices in half the time
					float Alpha = ((1 - dd * dd) * 0.3f + 0.5f) * rainLevel;
					int tex2 = (level->getLightColor(x, yl, z, 0) * 3 + 0xf000f0) / 4;
					t->tileRainQuad(x - xa + 0.5, yy0, z - za + 0.5, 0 * s + uo, yy0 * s / 4.0f + ra * s + vo,
									x + xa + 0.5, yy0, z + za + 0.5, 1 * s + uo, yy0 * s / 4.0f + ra * s + vo, 
									x + xa + 0.5, yy1, z + za + 0.5, 1 * s + uo, yy1 * s / 4.0f + ra * s + vo, 
									x - xa + 0.5, yy1, z - za + 0.5, 0 * s + uo, yy1 * s / 4.0f + ra * s + vo, 
									br, br, br, Alpha, br, br, br, Alpha, tex2);
#else
                    t->tex2((level->getLightColor(x, yl, z, 0) * 3 + 0xf000f0) / 4);
                    t->color(br, br, br, ((1 - dd * dd) * 0.3f + 0.5f) * rainLevel);
                    t->vertexUV(x - xa + 0.5, yy0, z - za + 0.5, 0 * s + uo, yy0 * s / 4.0f + ra * s + vo);
                    t->vertexUV(x + xa + 0.5, yy0, z + za + 0.5, 1 * s + uo, yy0 * s / 4.0f + ra * s + vo);
                    t->vertexUV(x + xa + 0.5, yy1, z + za + 0.5, 1 * s + uo, yy1 * s / 4.0f + ra * s + vo);
                    t->vertexUV(x - xa + 0.5, yy1, z - za + 0.5, 0 * s + uo, yy1 * s / 4.0f + ra * s + vo);
#endif
                    t->offset(0, 0, 0);
                }
			}
		}

	if( mode >= 0 ) t->end();
	glEnable(GL_CULL_FACE);
	glDisable(GL_BLEND);
	glAlphaFunc(GL_GREATER, 0.1f);
	this->turnOffLightLayer(a);

	RenderManager.StateSetEnableViewportClipPlanes(false);
}

// 4J - added forceScale parameter
void GameRenderer::setupGuiScreen(int forceScale /*=-1*/)
{
	ScreenSizeCalculator ssc(mc->options, mc->width, mc->height, forceScale);

	glClear(GL_DEPTH_BUFFER_BIT);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, (float)ssc.rawWidth, (float)ssc.rawHeight, 0, 1000, 3000);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glTranslatef(0, 0, -2000);
}

void GameRenderer::setupClearColor(float a)
{
	Level *level = mc->level;
	shared_ptr<Mob> player = mc->cameraTargetPlayer;

	float whiteness = 1.0f / (4 - mc->options->viewDistance);
	whiteness = 1 - (float) pow((double)whiteness, 0.25);

	Vec3 *skyColor = level->getSkyColor(mc->cameraTargetPlayer, a);
	float sr = (float) skyColor->x;
	float sg = (float) skyColor->y;
	float sb = (float) skyColor->z;

	Vec3 *fogColor = level->getFogColor(a);
	fr = (float) fogColor->x;
	fg = (float) fogColor->y;
	fb = (float) fogColor->z;

    if (mc->options->viewDistance < 2)
	{
		Vec3 *sunAngle = Mth::sin(level->getSunAngle(a)) > 0 ? Vec3::newTemp(-1, 0, 0) : Vec3::newTemp(1, 0, 0);
        float d = (float) player->getViewVector(a)->dot(sunAngle);
        if (d < 0) d = 0;
        if (d > 0)
		{
            float *c = level->dimension->getSunriseColor(level->getTimeOfDay(a), a);
            if (c != NULL)
			{
                d *= c[3];
                fr = fr * (1 - d) + c[0] * d;
                fg = fg * (1 - d) + c[1] * d;
                fb = fb * (1 - d) + c[2] * d;
            }
        }
    }

	fr += (sr - fr) * whiteness;
	fg += (sg - fg) * whiteness;
	fb += (sb - fb) * whiteness;

	float rainLevel = level->getRainLevel(a);
	if (rainLevel > 0)
	{
		float ba = 1 - rainLevel * 0.5f;
		float bb = 1 - rainLevel * 0.4f;
		fr *= ba;
		fg *= ba;
		fb *= bb;
	}
	float thunderLevel = level->getThunderLevel(a);
	if (thunderLevel > 0)
	{
		float ba = 1 - thunderLevel * 0.5f;
		fr *= ba;
		fg *= ba;
		fb *= ba;
	}

	int t = Camera::getBlockAt(mc->level, player, a);
	if (isInClouds)
	{
		Vec3 *cc = level->getCloudColor(a);
		fr = (float) cc->x;
		fg = (float) cc->y;
		fb = (float) cc->z;
	}
	else if (t != 0 && Tile::tiles[t]->material == Material::water)
	{
		
		unsigned int colour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Under_Water_Clear_Colour );
		byte redComponent = ((colour>>16)&0xFF);
		byte greenComponent = ((colour>>8)&0xFF);
		byte blueComponent = ((colour)&0xFF);

		fr = (float)redComponent/256;//0.02f;
		fg = (float)greenComponent/256;//0.02f;
		fb = (float)blueComponent/256;//0.2f;
	}
	else if (t != 0 && Tile::tiles[t]->material == Material::lava)
	{
		
		unsigned int colour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Under_Lava_Clear_Colour );
		byte redComponent = ((colour>>16)&0xFF);
		byte greenComponent = ((colour>>8)&0xFF);
		byte blueComponent = ((colour)&0xFF);

		fr = (float)redComponent/256;//0.6f;
		fg = (float)greenComponent/256;//0.1f;
		fb = (float)blueComponent/256;//0.00f;
	}

	float brr = fogBrO + (fogBr - fogBrO) * a;
	fr *= brr;
	fg *= brr;
	fb *= brr;

    double yy = (player->yOld + (player->y - player->yOld) * a) * level->dimension->getClearColorScale();		// 4J - getClearColorScale brought forward from 1.2.3

    if (player->hasEffect(MobEffect::blindness))
	{
        int duration = player->getEffect(MobEffect::blindness)->getDuration();
        if (duration < 20)
		{
            yy = yy * (1.0f - (float) duration / 20.0f);
        }
		else
		{
            yy = 0;
        }
    }

    if (yy < 1)
	{
        if (yy < 0) yy = 0;
        yy = yy * yy;
        fr *= yy;
        fg *= yy;
        fb *= yy;
    }

	if (player->hasEffect(MobEffect::nightVision))
	{
		float scale = getNightVisionScale(mc->player, a);
		{
			float dist = FLT_MAX;				// MGH - changed this to avoid divide by zero
			if ( (fr > 0) && (dist > (1.0f / fr)) )
			{
				dist = (1.0f / fr);
			}
			if ( (fg > 0) && (dist > (1.0f / fg)) )
			{
				dist = (1.0f / fg);
			}
			if ( (fb > 0) && (dist > (1.0f / fb)) )
			{
				dist = (1.0f / fb);
			}
			fr = fr * (1.0f - scale) + (fr * dist) * scale;
			fg = fg * (1.0f - scale) + (fg * dist) * scale;
			fb = fb * (1.0f - scale) + (fb * dist) * scale;
		}
	}

	if (mc->options->anaglyph3d)
	{
		float frr = (fr * 30 + fg * 59 + fb * 11) / 100;
		float fgg = (fr * 30 + fg * 70) / (100);
		float fbb = (fr * 30 + fb * 70) / (100);

		fr = frr;
		fg = fgg;
		fb = fbb;
	}

	glClearColor(fr, fg, fb, 0.0f);

}

void GameRenderer::setupFog(int i, float alpha)
{
	shared_ptr<Mob> player = mc->cameraTargetPlayer;

	// 4J - check for creative mode brought forward from 1.2.3
    bool creative = false;
    if (dynamic_pointer_cast<Player>(player) )
	{
        creative = (dynamic_pointer_cast<Player>(player))->abilities.instabuild;
    }

    if (i == 999)
	{
		__debugbreak();
		// 4J TODO
		/*
        glFog(GL_FOG_COLOR, getBuffer(0, 0, 0, 1));
        glFogi(GL_FOG_MODE, GL_LINEAR);
        glFogf(GL_FOG_START, 0);
        glFogf(GL_FOG_END, 8);

        if (GLContext.getCapabilities().GL_NV_fog_distance) {
            glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, NVFogDistance.GL_EYE_RADIAL_NV);
        }

        glFogf(GL_FOG_START, 0);
		*/
        return;
    }

	glFog(GL_FOG_COLOR, getBuffer(fr, fg, fb, 1));
	glNormal3f(0, -1, 0);
	glColor4f(1, 1, 1, 1);

    int t = Camera::getBlockAt(mc->level, player, alpha);

    if (player->hasEffect(MobEffect::blindness))
	{
        float distance = 5.0f;
        int duration = player->getEffect(MobEffect::blindness)->getDuration();
        if (duration < 20)
		{
            distance = 5.0f + (renderDistance - 5.0f) * (1.0f - (float) duration / 20.0f);
        }

        glFogi(GL_FOG_MODE, GL_LINEAR);
        if (i < 0)
		{
            glFogf(GL_FOG_START, 0);
            glFogf(GL_FOG_END, distance * 0.8f);
        }
		else
		{
            glFogf(GL_FOG_START, distance * 0.25f);
            glFogf(GL_FOG_END, distance);
        }
		// 4J - TODO investigate implementing this
//        if (GLContext.getCapabilities().GL_NV_fog_distance)
//		{
//            glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, NVFogDistance.GL_EYE_RADIAL_NV);
//        }
    }
	else if (isInClouds)
	{
		glFogi(GL_FOG_MODE, GL_EXP);
		glFogf(GL_FOG_DENSITY, 0.1f); // was 0.06
		
		unsigned int colour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_In_Cloud_Fog_Colour );
		byte redComponent = ((colour>>16)&0xFF);
		byte greenComponent = ((colour>>8)&0xFF);
		byte blueComponent = ((colour)&0xFF);

		float rr = (float)redComponent/256;//1.0f;
		float gg = (float)greenComponent/256;//1.0f;
		float bb = (float)blueComponent/256;//1.0f;

		if (mc->options->anaglyph3d)
		{
			float rrr = (rr * 30 + gg * 59 + bb * 11) / 100;
			float ggg = (rr * 30 + gg * 70) / (100);
			float bbb = (rr * 30 + bb * 70) / (100);

			rr = rrr;
			gg = ggg;
			bb = bbb;
		}
	}
	else if (t > 0 && Tile::tiles[t]->material == Material::water)
	{
		glFogi(GL_FOG_MODE, GL_EXP);
		if (player->hasEffect(MobEffect::waterBreathing))
		{
			glFogf(GL_FOG_DENSITY, 0.05f); // was 0.06
		}
		else
		{
			glFogf(GL_FOG_DENSITY, 0.1f); // was 0.06
		}
		
		unsigned int colour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Under_Water_Fog_Colour );
		byte redComponent = ((colour>>16)&0xFF);
		byte greenComponent = ((colour>>8)&0xFF);
		byte blueComponent = ((colour)&0xFF);

		float rr = (float)redComponent/256;//0.4f;
		float gg = (float)greenComponent/256;//0.4f;
		float bb = (float)blueComponent/256;//0.9f;

		if (mc->options->anaglyph3d)
		{
			float rrr = (rr * 30 + gg * 59 + bb * 11) / 100;
			float ggg = (rr * 30 + gg * 70) / (100);
			float bbb = (rr * 30 + bb * 70) / (100);

			rr = rrr;
			gg = ggg;
			bb = bbb;
		}
	}
	else if (t > 0 && Tile::tiles[t]->material == Material::lava)
	{
		glFogi(GL_FOG_MODE, GL_EXP);
		glFogf(GL_FOG_DENSITY, 2.0f); // was 0.06
		
		unsigned int colour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Under_Lava_Fog_Colour );
		byte redComponent = ((colour>>16)&0xFF);
		byte greenComponent = ((colour>>8)&0xFF);
		byte blueComponent = ((colour)&0xFF);

		float rr = (float)redComponent/256;//0.4f;
		float gg = (float)greenComponent/256;//0.3f;
		float bb = (float)blueComponent/256;//0.3f;

		if (mc->options->anaglyph3d)
		{
			float rrr = (rr * 30 + gg * 59 + bb * 11) / 100;
			float ggg = (rr * 30 + gg * 70) / (100);
			float bbb = (rr * 30 + bb * 70) / (100);

			rr = rrr;
			gg = ggg;
			bb = bbb;
		}
	}
	else
	{
        float distance = renderDistance;
		if (!mc->level->dimension->hasCeiling)
		{
			// 4J - test for doing bedrockfog brought forward from 1.2.3
			if (mc->level->dimension->hasBedrockFog() && !creative)
			{
				double yy = ((player->getLightColor(alpha) & 0xf00000) >> 20) / 16.0 + (player->yOld + (player->y - player->yOld) * alpha + 4) / 32;
				if (yy < 1)
				{
					if (yy < 0) yy = 0;
					yy = yy * yy;
					float dist = 100 * (float) yy;
					if (dist < 5) dist = 5;
					if (distance > dist) distance = dist;
				}
			}
		}

		glFogi(GL_FOG_MODE, GL_LINEAR);
		glFogf(GL_FOG_START, distance * 0.25f);
		glFogf(GL_FOG_END, distance);
		if (i < 0)
		{
			glFogf(GL_FOG_START, 0);
			glFogf(GL_FOG_END, distance * 0.8f);
        }
		else
		{
			glFogf(GL_FOG_START, distance * 0.25f);
			glFogf(GL_FOG_END, distance);
        }
		/* 4J - removed - TODO investigate
		if (GLContext.getCapabilities().GL_NV_fog_distance)
		{
			glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, NVFogDistance.GL_EYE_RADIAL_NV);
		}
		*/

		if (mc->level->dimension->isFoggyAt((int) player->x, (int) player->z))
		{
			glFogf(GL_FOG_START, distance * 0.05f);
			glFogf(GL_FOG_END, min(distance, 16 * 16 * .75f) * .5f);
		}
	}

	glEnable(GL_COLOR_MATERIAL);
	glColorMaterial(GL_FRONT, GL_AMBIENT);

}

FloatBuffer *GameRenderer::getBuffer(float a, float b, float c, float d)
{
	lb->clear();
	lb->put(a)->put(b)->put(c)->put(d);
	lb->flip();
	return lb;
}

int GameRenderer::getFpsCap(int option)
{
	int maxFps = 200;
	if (option == 1) maxFps = 120;
	if (option == 2) maxFps = 35;
	return maxFps;
}

void GameRenderer::updateAllChunks()
{
//	mc->levelRenderer->updateDirtyChunks(mc->cameraTargetPlayer, true);
}
