//	This file is part of Glest (www.glest.org)
//
//	Copyright (C) 2001-2008 Martiño Figueroa
//
//	You can redistribute this code and/or modify it under
//	the terms of the GNU General Public License as published
//	by the Free Software Foundation; either version 2 of the
//	License, or (at your option) any later version
// ==============================================================

#define NOMINMAX
#include "renderer.h"

#include "texture_gl.h"
#include "main_menu.h"
#include "config.h"
#include "components.h"
#include "time_flow.h"
#include "graphics_interface.h"
#include "object.h"
#include "core_data.h"
#include "game.h"
#include "metrics.h"
#include "opengl.h"
#include "faction.h"
#include "factory_repository.h"
#include <cstdlib>
#include "cache_manager.h"
#include "network_manager.h"
#include <algorithm>
#include <iterator>
#include "leak_dumper.h"

using namespace Shared::Graphics;
using namespace Shared::Graphics::Gl;
using namespace Shared::Util;
using namespace Shared::Graphics;

namespace Glest { namespace Game{

uint32 Renderer::SurfaceData::nextUniqueId = 1;
bool Renderer::renderText3DEnabled = true;

//const float SKIP_INTERPOLATION_DISTANCE = 20.0f;
const string DEFAULT_CHAR_FOR_WIDTH_CALC = "V";

enum PROJECTION_TO_INFINITY {
	pti_D_IS_ZERO,
	pti_N_OVER_D_IS_OUTSIDE
};

// =====================================================
// 	class MeshCallbackTeamColor
// =====================================================

bool MeshCallbackTeamColor::noTeamColors = false;

void MeshCallbackTeamColor::execute(const Mesh *mesh) {
	//team color
	if( mesh->getCustomTexture() && teamTexture != NULL &&
		MeshCallbackTeamColor::noTeamColors == false) {
		//texture 0
		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

		//set color to interpolation
		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);

		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);

		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE1);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);

		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_TEXTURE);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);

		//set alpha to 1
		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);

		//texture 1
		glActiveTexture(GL_TEXTURE1);
		glMultiTexCoord2f(GL_TEXTURE1, 0.f, 0.f);
		glEnable(GL_TEXTURE_2D);

		glBindTexture(GL_TEXTURE_2D, static_cast<const Texture2DGl*>(teamTexture)->getHandle());
		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);

		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);

		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);

		//set alpha to 1
		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);

		glActiveTexture(GL_TEXTURE0);
	}
	else {
		glActiveTexture(GL_TEXTURE1);
		glDisable(GL_TEXTURE_2D);
		glActiveTexture(GL_TEXTURE0);
		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	}
}

// ===========================================================
//	class Renderer
// ===========================================================

// ===================== PUBLIC ========================

const int Renderer::maxProgressBar= 100;
const Vec4f Renderer::progressBarBack1= Vec4f(0.7f, 0.7f, 0.7f, 0.7f);
const Vec4f Renderer::progressBarBack2= Vec4f(0.7f, 0.7f, 0.7f, 1.f);
const Vec4f Renderer::progressBarFront1= Vec4f(0.f, 0.5f, 0.f, 1.f);
const Vec4f Renderer::progressBarFront2= Vec4f(0.f, 0.1f, 0.f, 1.f);

const float Renderer::sunDist= 10e6;
const float Renderer::moonDist= 10e6;
const float Renderer::lightAmbFactor= 0.4f;

const int Renderer::maxMouse2dAnim= 100;

const GLenum Renderer::baseTexUnit= GL_TEXTURE0;
const GLenum Renderer::fowTexUnit= GL_TEXTURE1;
const GLenum Renderer::shadowTexUnit= GL_TEXTURE2;

const float Renderer::selectionCircleRadius= 0.7f;
const float Renderer::magicCircleRadius= 1.f;

//perspective values
const float Renderer::perspFov= 60.f;
const float Renderer::perspNearPlane= 1.f;
//const float Renderer::perspFarPlane= 50.f;
float Renderer::perspFarPlane= 1000000.f;

const float Renderer::ambFactor= 0.7f;
const Vec4f Renderer::fowColor= Vec4f(0.0f, 0.0f, 0.0f, 1.0f);
const Vec4f Renderer::defSpecularColor= Vec4f(0.8f, 0.8f, 0.8f, 1.f);
const Vec4f Renderer::defDiffuseColor= Vec4f(1.f, 1.f, 1.f, 1.f);
const Vec4f Renderer::defAmbientColor= Vec4f(1.f * ambFactor, 1.f * ambFactor, 1.f * ambFactor, 1.f);
const Vec4f Renderer::defColor= Vec4f(1.f, 1.f, 1.f, 1.f);

//const float Renderer::maxLightDist= 100.f;
const float Renderer::maxLightDist= 100.f;

bool Renderer::rendererEnded = true;

const int MIN_FPS_NORMAL_RENDERING = 15;
const int MIN_FPS_NORMAL_RENDERING_TOP_THRESHOLD = 25;

const int OBJECT_SELECT_OFFSET=100000000;

bool VisibleQuadContainerCache::enableFrustumCalcs = true;

// ==================== constructor and destructor ====================

Renderer::Renderer() : BaseRenderer(), saveScreenShotThreadAccessor(new Mutex(CODE_AT_LINE)) {
	//this->masterserverMode = masterserverMode;
	//printf("this->masterserverMode = %d\n",this->masterserverMode);
	//assert(0==1);

	Renderer::rendererEnded = false;
	shadowIntensity = 0;
	shadowFrameSkip = 0;
	triangleCount = 0;
	smoothedRenderFps = 0;
	shadowTextureSize = 0;
	shadows = sDisabled;
	shadowMapFrame = 0;
	textures3D = false;
	photoMode = false;
	focusArrows = false;
	pointCount = 0;
	maxLights = 0;
	waterAnim = 0;

	this->allowRenderUnitTitles = false;
	this->menu = NULL;
	this->game = NULL;
	this->gameCamera = NULL;
	showDebugUI = false;
	showDebugUILevel = debugui_fps;
	modelRenderer = NULL;
	textRenderer = NULL;
	textRenderer3D = NULL;
	particleRenderer = NULL;
	saveScreenShotThread = NULL;
	mapSurfaceData.clear();
	visibleFrameUnitList.clear();
	visibleFrameUnitListCameraKey = "";

	quadCache = VisibleQuadContainerCache();
	quadCache.clearFrustumData();

	lastRenderFps=MIN_FPS_NORMAL_RENDERING;
	shadowsOffDueToMinRender=false;
	shadowMapHandle=0;
	shadowMapHandleValid=false;

	//list3d=0;
	//list3dValid=false;
	//list2d=0;
	//list2dValid=false;
	//list3dMenu=0;
	//list3dMenuValid=false;
	//customlist3dMenu=NULL;
	//this->mm3d = NULL;
	this->custom_mm3d = NULL;

	this->program = NULL;

	//resources
	for(int i=0; i < rsCount; ++i) {
		modelManager[i] = NULL;
		textureManager[i] = NULL;
		particleManager[i] = NULL;
		fontManager[i] = NULL;
	}

	Config &config= Config::getInstance();

	Renderer::perspFarPlane = config.getFloat("PerspectiveFarPlane",floatToStr(Renderer::perspFarPlane).c_str());
	this->no2DMouseRendering = config.getBool("No2DMouseRendering","false");
	this->maxConsoleLines= config.getInt("ConsoleMaxLines");

	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s Line: %d] Renderer::perspFarPlane [%f] this->no2DMouseRendering [%d] this->maxConsoleLines [%d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,Renderer::perspFarPlane,this->no2DMouseRendering,this->maxConsoleLines);

	GraphicsInterface &gi= GraphicsInterface::getInstance();
	FactoryRepository &fr= FactoryRepository::getInstance();
	gi.setFactory(fr.getGraphicsFactory(config.getString("FactoryGraphics")));
	GraphicsFactory *graphicsFactory= GraphicsInterface::getInstance().getFactory();

	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == false) {
		modelRenderer= graphicsFactory->newModelRenderer();
		textRenderer= graphicsFactory->newTextRenderer2D();
		textRenderer3D = graphicsFactory->newTextRenderer3D();
		particleRenderer= graphicsFactory->newParticleRenderer();
	}

	//resources
	for(int i=0; i< rsCount; ++i) {
		if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == false) {
			modelManager[i]= graphicsFactory->newModelManager();
			textureManager[i]= graphicsFactory->newTextureManager();
			modelManager[i]->setTextureManager(textureManager[i]);
			fontManager[i]= graphicsFactory->newFontManager();
		}
		particleManager[i]= graphicsFactory->newParticleManager();
	}

	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == false) {
		static string mutexOwnerId = string(extractFileFromDirectoryPath(__FILE__).c_str()) + string("_") + intToStr(__LINE__);
		saveScreenShotThread = new SimpleTaskThread(this,0,25);
		saveScreenShotThread->setUniqueID(mutexOwnerId);
		saveScreenShotThread->start();
	}
}

void Renderer::cleanupScreenshotThread() {
    if(saveScreenShotThread) {
		saveScreenShotThread->signalQuit();
//		for(time_t elapsed = time(NULL);
//			getSaveScreenQueueSize() > 0 && difftime((long int)time(NULL),elapsed) <= 7;) {
//			sleep(0);
//		}
//		if(saveScreenShotThread->canShutdown(true) == true &&
//				saveScreenShotThread->shutdownAndWait() == true) {
//			//printf("IN MenuStateCustomGame cleanup - C\n");
//			delete saveScreenShotThread;
//		}
//		saveScreenShotThread = NULL;
		if(saveScreenShotThread->shutdownAndWait() == true) {
			delete saveScreenShotThread;
		}
		saveScreenShotThread = NULL;


		if(getSaveScreenQueueSize() > 0) {
			if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line %d] FORCING MEMORY CLEANUP and NOT SAVING screenshots, saveScreenQueue.size() = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,saveScreenQueue.size());

			static string mutexOwnerId = string(extractFileFromDirectoryPath(__FILE__).c_str()) + string("_") + intToStr(__LINE__);
			MutexSafeWrapper safeMutex(saveScreenShotThreadAccessor,mutexOwnerId);
			for(std::list<std::pair<string,Pixmap2D *> >::iterator iter = saveScreenQueue.begin();
				iter != saveScreenQueue.end(); ++iter) {
				delete iter->second;
			}
			saveScreenQueue.clear();
		}
	}
}

Renderer::~Renderer() {
	try{
		if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

		delete modelRenderer;
		modelRenderer = NULL;
		delete textRenderer;
		textRenderer = NULL;
		delete textRenderer3D;
		textRenderer3D = NULL;
		delete particleRenderer;
		particleRenderer = NULL;

		if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

		//resources
		for(int i=0; i<rsCount; ++i){
			delete modelManager[i];
			modelManager[i] = NULL;
			delete textureManager[i];
			textureManager[i] = NULL;
			delete particleManager[i];
			particleManager[i] = NULL;
			delete fontManager[i];
			fontManager[i] = NULL;
		}

		if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

		// Wait for the queue to become empty or timeout the thread at 7 seconds
		cleanupScreenshotThread();

		if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

		mapSurfaceData.clear();
		quadCache = VisibleQuadContainerCache();
		quadCache.clearFrustumData();

		if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

		this->menu = NULL;
		this->game = NULL;
		this->gameCamera = NULL;

		delete saveScreenShotThreadAccessor;
		saveScreenShotThreadAccessor = NULL;
	}
	catch(const exception &e) {
		char szBuf[8096]="";
		snprintf(szBuf,8096,"In [%s::%s Line: %d]\nError [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,e.what());
		SystemFlags::OutputDebug(SystemFlags::debugError,szBuf);
		if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,szBuf);

		throw megaglest_runtime_error(szBuf);
	}
}

void Renderer::simpleTask(BaseThread *callingThread,void *userdata) {
	// This code reads pixmaps from a queue and saves them to disk
	Pixmap2D *savePixMapBuffer=NULL;
	string path="";
	static string mutexOwnerId = string(extractFileFromDirectoryPath(__FILE__).c_str()) + string("_") + intToStr(__LINE__);
	MutexSafeWrapper safeMutex(saveScreenShotThreadAccessor,mutexOwnerId);
	if(saveScreenQueue.empty() == false) {
		if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line %d] saveScreenQueue.size() = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,saveScreenQueue.size());

		savePixMapBuffer = saveScreenQueue.front().second;
		path = saveScreenQueue.front().first;

		saveScreenQueue.pop_front();
	}
	safeMutex.ReleaseLock();

	if(savePixMapBuffer != NULL) {
		if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line %d] about to save [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,path.c_str());

		savePixMapBuffer->save(path);
		delete savePixMapBuffer;
	}
}

bool Renderer::isEnded() {
	return Renderer::rendererEnded;
}

Renderer &Renderer::getInstance() {
	static Renderer renderer;
	return renderer;
}

void Renderer::reinitAll() {
	//resources
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}
	for(int i=0; i<rsCount; ++i){
		//modelManager[i]->init();
		textureManager[i]->init(true);
		//particleManager[i]->init();
		//fontManager[i]->init();
	}
}
// ==================== init ====================

void Renderer::init() {
	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	Config &config= Config::getInstance();

	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	loadConfig();

	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	if(config.getBool("CheckGlCaps")){

		if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

		checkGlCaps();
	}

	if(glActiveTexture == NULL) {
		char szBuf[8096]="";
		snprintf(szBuf,8096,"Error: glActiveTexture == NULL\nglActiveTexture is only supported if the GL version is 1.3 or greater,\nor if the ARB_multitexture extension is supported!");
		throw megaglest_runtime_error(szBuf);
	}

	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	if(config.getBool("FirstTime")){
		if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

		config.setBool("FirstTime", false);
		autoConfig();

		if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

		config.save();
	}
	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	modelManager[rsGlobal]->init();

	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	textureManager[rsGlobal]->init();

	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	fontManager[rsGlobal]->init();

	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	init2dList();

	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	glHint(GL_FOG_HINT, GL_FASTEST);
	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
	//glHint(GL_GENERATE_MIPMAP_HINT, GL_FASTEST);
	glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST);
	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
	//glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
	//glHint(GL_POINT_SMOOTH_HINT, GL_FASTEST);

	//glHint(GL_POLYGON_SMOOTH_HINT, GL_FASTEST);
	glHint(GL_TEXTURE_COMPRESSION_HINT, GL_FASTEST);
	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

}

void Renderer::initGame(const Game *game, GameCamera *gameCamera) {
	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
	this->gameCamera = gameCamera;
	VisibleQuadContainerCache::enableFrustumCalcs = Config::getInstance().getBool("EnableFrustrumCalcs","true");
	quadCache = VisibleQuadContainerCache();
	quadCache.clearFrustumData();

	SurfaceData::nextUniqueId = 1;
	mapSurfaceData.clear();
	this->game= game;
	worldToScreenPosCache.clear();

	//vars
	shadowMapFrame= 0;
	waterAnim= 0;

	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	//check gl caps
	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
	checkGlOptionalCaps();

	//shadows
	if(shadows == sProjected || shadows == sShadowMapping) {
		static_cast<ModelRendererGl*>(modelRenderer)->setSecondaryTexCoordUnit(2);
		Config &config= Config::getInstance();

		glGenTextures(1, &shadowMapHandle);
		shadowMapHandleValid=true;

		shadowIntensity= config.getFloat("ShadowIntensity","1.0");
		if(game!=NULL){
			shadowIntensity=shadowIntensity*game->getWorld()->getTileset()->getShadowIntense();
			if(shadowIntensity > 1.0f){
				shadowIntensity=1.0f;
			}
		}

		glBindTexture(GL_TEXTURE_2D, shadowMapHandle);

		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

		if(shadows == sShadowMapping) {
			if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

			//shadow mapping
			glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE);
			//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
			//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FAIL_VALUE_ARB, 1.0f-shadowAlpha);

			glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32,
				shadowTextureSize, shadowTextureSize,
				0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
		}
		else {
			if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

			//projected
			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8,
				shadowTextureSize, shadowTextureSize,
				0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL);
		}

		shadowMapFrame= -1;
	}

	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	IF_DEBUG_EDITION( getDebugRenderer().init(); )

	//texture init
	modelManager[rsGame]->init();
	textureManager[rsGame]->init();
	fontManager[rsGame]->init();

	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	init3dList();
}

void Renderer::manageDeferredParticleSystems() {

//	if(deferredParticleSystems.empty() == false) {
//		printf("deferredParticleSystems.size() = %d\n",(int)deferredParticleSystems.size());
//	}

	for(unsigned int i = 0; i < deferredParticleSystems.size(); ++i) {
		std::pair<ParticleSystem *, ResourceScope> &deferredParticleSystem = deferredParticleSystems[i];
		ParticleSystem *ps = deferredParticleSystem.first;
		ResourceScope rs = deferredParticleSystem.second;
		if(ps->getTextureFileLoadDeferred() != "" && ps->getTexture() == NULL) {
			CoreData::TextureSystemType textureSystemId =
					static_cast<CoreData::TextureSystemType>(
							ps->getTextureFileLoadDeferredSystemId());

			//printf("Load DEFERRED particle i = %d textureSystemId = %d\n",i,textureSystemId);

			if(textureSystemId != CoreData::tsyst_NONE) {
				Texture2D *texture= CoreData::getInstance().getTextureBySystemId(textureSystemId);
				//printf("Loading texture from system [%d] [%p]\n",textureSystemId,texture);
				ps->setTexture(texture);

				//printf("#2 Load DEFERRED particle i = %d textureSystemId = %d, texture = %p\n",i,textureSystemId,texture);
			}
			else {
				Texture2D *texture= newTexture2D(rs);
				if(texture) {
					texture->setFormat(ps->getTextureFileLoadDeferredFormat());
					texture->getPixmap()->init(ps->getTextureFileLoadDeferredComponents());
				}
				if(texture) {
					string textureFile = ps->getTextureFileLoadDeferred();
					if(fileExists(textureFile) == false) {
						textureFile = Config::findValidLocalFileFromPath(textureFile);
					}
					texture->load(textureFile);
					ps->setTexture(texture);
				}
				//printf("#3 Load DEFERRED particle i = %d textureSystemId = %d, texture = %p\n",i,textureSystemId,texture);
			}
		}
		if(dynamic_cast<GameParticleSystem *>(ps) != NULL) {
			GameParticleSystem *gps = dynamic_cast<GameParticleSystem *>(ps);
			if(gps != NULL && gps->getModelFileLoadDeferred() != "" && gps->getModel() == NULL) {
                std::map<string,vector<pair<string, string> > > loadedFileList;
                Model *model= newModel(rsGame, gps->getModelFileLoadDeferred(), false, &loadedFileList, NULL);
                if(model)
                    gps->setModel(model);
			}
		}
		manageParticleSystem(ps, rs);
		//printf("Managing ps [%p]\n",ps);
	}
	deferredParticleSystems.clear();
	//printf("After deferredParticleSystems.size() = %d\n",deferredParticleSystems.size());
}

void Renderer::initMenu(const MainMenu *mm) {
	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	this->menu = mm;

	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	modelManager[rsMenu]->init();
	textureManager[rsMenu]->init();
	fontManager[rsMenu]->init();
	//modelRenderer->setCustomTexture(CoreData::getInstance().getCustomTexture());

	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	//init3dListMenu(mm);

	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
}

void Renderer::reset3d() {
	assertGl();
	glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
	//glCallList(list3d);
	render3dSetup();

	pointCount= 0;
	triangleCount= 0;
	assertGl();
}

void Renderer::reset2d() {
	assertGl();
	glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SINGLE_COLOR);
	//glCallList(list2d);
	render2dMenuSetup();
	assertGl();
}

void Renderer::reset3dMenu() {
	assertGl();
	glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SINGLE_COLOR);

	//printf("In [%s::%s Line: %d] this->custom_mm3d [%p] this->mm3d [%p]\n",__FILE__,__FUNCTION__,__LINE__,this->custom_mm3d,this->mm3d);

	if(this->custom_mm3d != NULL) {
		render3dMenuSetup(this->custom_mm3d);
		//glCallList(*this->customlist3dMenu);
	}
	else {
		render3dMenuSetup(this->menu);
		//render3dMenuSetup(this->mm3d);
		//glCallList(list3dMenu);
	}

	assertGl();
}

// ==================== end ====================

void Renderer::end() {
	quadCache = VisibleQuadContainerCache();
	quadCache.clearFrustumData();

	if(Renderer::rendererEnded == true) {
		return;
	}
	std::map<string,Texture2D *> &crcFactionPreviewTextureCache = CacheManager::getCachedItem< std::map<string,Texture2D *> >(GameConstants::factionPreviewTextureCacheLookupKey);
	crcFactionPreviewTextureCache.clear();

	// Wait for the queue to become empty or timeout the thread at 7 seconds
	cleanupScreenshotThread();

	mapSurfaceData.clear();

	//delete resources
	if(modelManager[rsGlobal]) {
		modelManager[rsGlobal]->end();
	}
	if(textureManager[rsGlobal]) {
		textureManager[rsGlobal]->end();
	}
	if(fontManager[rsGlobal]) {
		fontManager[rsGlobal]->end();
	}
	if(particleManager[rsGlobal]) {
		particleManager[rsGlobal]->end();
	}

	//delete 2d list
	//if(list2dValid == true) {
	//	glDeleteLists(list2d, 1);
	//	list2dValid=false;
	//}

	Renderer::rendererEnded = true;
}

void Renderer::endScenario() {
	this->game= NULL;
	this->gameCamera = NULL;
	quadCache = VisibleQuadContainerCache();
	quadCache.clearFrustumData();

	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	//delete resources
	//modelManager[rsGame]->end();
	//textureManager[rsGame]->end();
	//fontManager[rsGame]->end();
	//particleManager[rsGame]->end();

	if(shadowMapHandleValid == true &&
		(shadows == sProjected || shadows == sShadowMapping)) {
		glDeleteTextures(1, &shadowMapHandle);
		shadowMapHandleValid=false;
	}

	//if(list3dValid == true) {
	//	glDeleteLists(list3d, 1);
	//	list3dValid=false;
	//}

	worldToScreenPosCache.clear();
	ReleaseSurfaceVBOs();
	mapSurfaceData.clear();
}

void Renderer::endGame(bool isFinalEnd) {
	this->game= NULL;
	this->gameCamera = NULL;
	Config &config= Config::getInstance();

	try {
		quadCache = VisibleQuadContainerCache();
		quadCache.clearFrustumData();
	}
	catch(const exception &e) {
		char szBuf[8096]="";
		snprintf(szBuf,8096,"In [%s::%s Line: %d]\nError [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,e.what());
		SystemFlags::OutputDebug(SystemFlags::debugError,szBuf);
		if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,szBuf);

		abort();
	}

	if(isFinalEnd) {
		//delete resources
		if(modelManager[rsGame] != NULL) {
			modelManager[rsGame]->end();
		}
		if(textureManager[rsGame] != NULL) {
			textureManager[rsGame]->end();
		}
		if(fontManager[rsGame] != NULL) {
			fontManager[rsGame]->end();
		}
		if(particleManager[rsGame] != NULL) {
			particleManager[rsGame]->end();
		}
	}

	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	if(shadowMapHandleValid == true &&
		(shadows == sProjected || shadows == sShadowMapping)) {
		glDeleteTextures(1, &shadowMapHandle);
		shadowMapHandleValid=false;
	}
	shadowIntensity= config.getFloat("ShadowIntensity","1.0");

	//if(list3dValid == true) {
	//	glDeleteLists(list3d, 1);
	//	list3dValid=false;
	//}

	worldToScreenPosCache.clear();
	ReleaseSurfaceVBOs();
	mapSurfaceData.clear();
}

void Renderer::endMenu() {
	this->menu = NULL;

	//delete resources
	if(modelManager[rsMenu]) {
		modelManager[rsMenu]->end();
	}
	if(textureManager[rsMenu]) {
		textureManager[rsMenu]->end();
	}
	if(fontManager[rsMenu]) {
		fontManager[rsMenu]->end();
	}
	if(particleManager[rsMenu]) {
		particleManager[rsMenu]->end();
	}

	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	//if(this->customlist3dMenu != NULL) {
	//	glDeleteLists(*this->customlist3dMenu,1);
	//}
	//else {
	//	glDeleteLists(list3dMenu, 1);
	//}
}

void Renderer::reloadResources() {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	for(int i=0; i<rsCount; ++i) {
		modelManager[i]->end();
		textureManager[i]->end();
		fontManager[i]->end();
	}

	for(int i=0; i<rsCount; ++i) {
		modelManager[i]->init();
		textureManager[i]->init();
		fontManager[i]->init();
	}
}

// ==================== engine interface ====================

void Renderer::initTexture(ResourceScope rs, Texture *texture) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	textureManager[rs]->initTexture(texture);
}

void Renderer::endTexture(ResourceScope rs, Texture *texture, bool mustExistInList) {
	string textureFilename = texture->getPath();

	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s Line: %d] free texture from manager [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,textureFilename.c_str());

	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	textureManager[rs]->endTexture(texture,mustExistInList);

	if(rs == rsGlobal) {
		std::map<string,Texture2D *> &crcFactionPreviewTextureCache = CacheManager::getCachedItem< std::map<string,Texture2D *> >(GameConstants::factionPreviewTextureCacheLookupKey);
		if(crcFactionPreviewTextureCache.find(textureFilename) != crcFactionPreviewTextureCache.end()) {
			if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] textureFilename [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,textureFilename.c_str());
			if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s Line: %d] free texture from cache [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,textureFilename.c_str());

			crcFactionPreviewTextureCache.erase(textureFilename);
		}
	}
}
void Renderer::endLastTexture(ResourceScope rs, bool mustExistInList) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	textureManager[rs]->endLastTexture(mustExistInList);
}

Model *Renderer::newModel(ResourceScope rs,const string &path,bool deletePixMapAfterLoad,std::map<string,vector<pair<string, string> > > *loadedFileList, string *sourceLoader){
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return NULL;
	}

	return modelManager[rs]->newModel(path,deletePixMapAfterLoad,loadedFileList,sourceLoader);
}

void Renderer::endModel(ResourceScope rs, Model *model,bool mustExistInList) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	modelManager[rs]->endModel(model,mustExistInList);
}
void Renderer::endLastModel(ResourceScope rs, bool mustExistInList) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	modelManager[rs]->endLastModel(mustExistInList);
}

Texture2D *Renderer::newTexture2D(ResourceScope rs){
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return NULL;
	}

	return textureManager[rs]->newTexture2D();
}

Texture3D *Renderer::newTexture3D(ResourceScope rs){
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return NULL;
	}

	return textureManager[rs]->newTexture3D();
}

Font2D *Renderer::newFont(ResourceScope rs){
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return NULL;
	}

	return fontManager[rs]->newFont2D();
}

Font3D *Renderer::newFont3D(ResourceScope rs){
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return NULL;
	}

	return fontManager[rs]->newFont3D();
}

void Renderer::endFont(::Shared::Graphics::Font *font, ResourceScope rs, bool mustExistInList) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	fontManager[rs]->endFont(font,mustExistInList);
}

void Renderer::resetFontManager(ResourceScope rs) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}
	fontManager[rs]->end();
	fontManager[rsGlobal]->init();
}

void Renderer::addToDeferredParticleSystemList(std::pair<ParticleSystem *, ResourceScope> deferredParticleSystem) {
	deferredParticleSystems.push_back(deferredParticleSystem);
}

void Renderer::manageParticleSystem(ParticleSystem *particleSystem, ResourceScope rs){
	particleManager[rs]->manage(particleSystem);
}

bool Renderer::validateParticleSystemStillExists(ParticleSystem * particleSystem,ResourceScope rs) const {
	return particleManager[rs]->validateParticleSystemStillExists(particleSystem);
}

void Renderer::removeParticleSystemsForParticleOwner(ParticleOwner * particleOwner,ResourceScope rs) {
	particleManager[rs]->removeParticleSystemsForParticleOwner(particleOwner);
}

void Renderer::cleanupParticleSystems(vector<ParticleSystem *> &particleSystems, ResourceScope rs) {
	particleManager[rs]->cleanupParticleSystems(particleSystems);
}

void Renderer::cleanupUnitParticleSystems(vector<UnitParticleSystem *> &particleSystems, ResourceScope rs) {
	particleManager[rs]->cleanupUnitParticleSystems(particleSystems);
}

void Renderer::updateParticleManager(ResourceScope rs, int renderFps) {
	particleManager[rs]->update(renderFps);
}

void Renderer::renderParticleManager(ResourceScope rs){
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	glPushAttrib(GL_DEPTH_BUFFER_BIT  | GL_STENCIL_BUFFER_BIT);
	glDepthFunc(GL_LESS);
	particleRenderer->renderManager(particleManager[rs], modelRenderer);
	glPopAttrib();
}

void Renderer::swapBuffers() {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}
	//glFlush(); // should not be required - http://www.opengl.org/wiki/Common_Mistakes
	//glFlush();

	GraphicsInterface::getInstance().getCurrentContext()->swapBuffers();
}

// ==================== lighting ====================

//places all the opengl lights
void Renderer::setupLighting() {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

    int lightCount= 0;
	const World *world= game->getWorld();
	const GameCamera *gameCamera= game->getGameCamera();
	const TimeFlow *timeFlow= world->getTimeFlow();
	float time= timeFlow->getTime();

	assertGl();

    //sun/moon light
	Vec3f lightColor= timeFlow->computeLightColor();
	Vec3f fogColor= world->getTileset()->getFogColor();
	Vec4f lightPos= timeFlow->isDay()? computeSunPos(time): computeMoonPos(time);
	nearestLightPos= lightPos;

	glLightfv(GL_LIGHT0, GL_POSITION, lightPos.ptr());
	glLightfv(GL_LIGHT0, GL_AMBIENT, Vec4f(lightColor*lightAmbFactor, 1.f).ptr());
	glLightfv(GL_LIGHT0, GL_DIFFUSE, Vec4f(lightColor, 1.f).ptr());
	glLightfv(GL_LIGHT0, GL_SPECULAR, Vec4f(0.0f, 0.0f, 0.f, 1.f).ptr());

	glFogfv(GL_FOG_COLOR, Vec4f(fogColor*lightColor, 1.f).ptr());

    lightCount++;

	//disable all secondary lights
	for(int i= 1; i < maxLights; ++i) {
		glDisable(GL_LIGHT0 + i);
	}

    //unit lights (not projectiles)

	if(timeFlow->isTotalNight()) {
		VisibleQuadContainerCache &qCache = getQuadCache();
		if(qCache.visibleQuadUnitList.empty() == false) {
			//bool modelRenderStarted = false;
			for(int visibleUnitIndex = 0;
					visibleUnitIndex < (int)qCache.visibleQuadUnitList.size() && lightCount < maxLights;
					++visibleUnitIndex) {
				Unit *unit = qCache.visibleQuadUnitList[visibleUnitIndex];

				if(world->toRenderUnit(unit) &&
					unit->getCurrMidHeightVector().dist(gameCamera->getPos()) < maxLightDist &&
					unit->getType()->getLight() && unit->isOperative()) {
					//printf("$$$ Show light for faction: %s # %d / %d for Unit [%d - %s]\n",world->getFaction(i)->getType()->getName().c_str(),lightCount,maxLights,unit->getId(),unit->getFullName().c_str());

					Vec4f pos= Vec4f(unit->getCurrMidHeightVector());
					pos.y+=4.f;

					GLenum lightEnum= GL_LIGHT0 + lightCount;

					glEnable(lightEnum);
					glLightfv(lightEnum, GL_POSITION, pos.ptr());
					glLightfv(lightEnum, GL_AMBIENT, Vec4f(unit->getType()->getLightColor()).ptr());
					glLightfv(lightEnum, GL_DIFFUSE, Vec4f(unit->getType()->getLightColor()).ptr());
					glLightfv(lightEnum, GL_SPECULAR, Vec4f(unit->getType()->getLightColor()*0.3f).ptr());
					glLightf(lightEnum, GL_QUADRATIC_ATTENUATION, 0.05f);

					++lightCount;

					const GameCamera *gameCamera= game->getGameCamera();

					if(Vec3f(pos).dist(gameCamera->getPos())<Vec3f(nearestLightPos).dist(gameCamera->getPos())){
						nearestLightPos= pos;
					}
				}
			}
		}
	}

	assertGl();
}

void Renderer::setupLightingForRotatedModel() {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	const World *world= game->getWorld();
	//const GameCamera *gameCamera= game->getGameCamera();
	const TimeFlow *timeFlow= world->getTimeFlow();
	float time= timeFlow->getTime();

	assertGl();

    //sun/moon light
	Vec3f lightColor= timeFlow->computeLightColor();
	Vec3f fogColor= world->getTileset()->getFogColor();
	Vec4f lightPos= timeFlow->isDay()? computeSunPos(time): computeMoonPos(time);
	//nearestLightPos= lightPos;

	glLightfv(GL_LIGHT0, GL_POSITION, lightPos.ptr());
	glLightfv(GL_LIGHT0, GL_AMBIENT, Vec4f(lightColor*lightAmbFactor, 1.f).ptr());
	glLightfv(GL_LIGHT0, GL_DIFFUSE, Vec4f(lightColor, 1.f).ptr());
	glLightfv(GL_LIGHT0, GL_SPECULAR, Vec4f(0.0f, 0.0f, 0.f, 1.f).ptr());

	glFogfv(GL_FOG_COLOR, Vec4f(fogColor*lightColor, 1.f).ptr());

	assertGl();
}

void Renderer::loadGameCameraMatrix() {
	const GameCamera *gameCamera= game->getGameCamera();

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	if(gameCamera != NULL) {
		glRotatef(gameCamera->getVAng(), -1, 0, 0);
		glRotatef(gameCamera->getHAng(), 0, 1, 0);
		glTranslatef(-(gameCamera->getPos().x + gameCamera->getShakeOffset().x),
				-gameCamera->getPos().y,
				-(gameCamera->getPos().z + gameCamera->getShakeOffset().y));
	}
}

void Renderer::loadCameraMatrix(const Camera *camera) {
	const Vec3f &position= camera->getConstPosition();
	Quaternion orientation= camera->getOrientation().conjugate();

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glMultMatrixf(orientation.toMatrix4().ptr());
	glTranslatef(-position.x, -position.y, -position.z);
}

static Vec2i _unprojectMap(const Vec2i& pt,const GLdouble* model,const GLdouble* projection,const GLint* viewport,const char* label=NULL) {
	Vec3d a,b;
	/*  note viewport[3] is height of window in pixels  */
	GLint realy = viewport[3] - (GLint) pt.y;
	gluUnProject(pt.x,realy,0,model,projection,viewport,&a.x,&a.y,&a.z);
	gluUnProject(pt.x,realy,1,model,projection,viewport,&b.x,&b.y,&b.z);

	// junk values if you were looking parallel to the XZ plane; this shouldn't happen as the camera can't do this?
	const Vec3f
		start(a.x,a.y,a.z),
		stop(b.x,b.y,b.z),
		plane(0,0,0),
		norm(0,1,0),
		u = stop-start,
		w = start-plane;
	const float d = norm.x*u.x + norm.y*u.y + norm.z*u.z;
	if(std::fabs(d) < 0.00001)
		throw pti_D_IS_ZERO;

	const float nd = -(norm.x*w.x + norm.y*w.y + norm.z*w.z) / d;
	if(nd < 0.0 || nd >= 1.0)
		throw pti_N_OVER_D_IS_OUTSIDE;

	const Vec3f i = start + u*nd;
	//const Vec2i pos(i.x,i.z);

	Vec2i pos;
	if(strcmp(label,"tl") == 0) {
		pos = Vec2i(std::floor(i.x),std::floor(i.z));
	}
	else if(strcmp(label,"tr") == 0) {
		pos = Vec2i(std::ceil(i.x),std::floor(i.z));
	}
	else if(strcmp(label,"bl") == 0) {
		pos = Vec2i(std::floor(i.x),std::ceil(i.z));
	}
	else if(strcmp(label,"br") == 0) {
		pos = Vec2i(std::ceil(i.x),std::ceil(i.z));
	}

	if(false) { // print debug info
		if(label) printf("%s ",label);
		printf("%d,%d -> %f,%f,%f -> %f,%f,%f -> %f,%f,%f -> %d,%d\n",
			pt.x,pt.y,
			start.x,start.y,start.z,
			stop.x,stop.y,stop.z,
			i.x,i.y,i.z,
			pos.x,pos.y);
	}
	return pos;

}

bool Renderer::ExtractFrustum(VisibleQuadContainerCache &quadCacheItem) {
   bool frustumChanged = false;
   vector<float> proj(16,0);
   vector<float> modl(16,0);

   /* Get the current PROJECTION matrix from OpenGL */
   glGetFloatv( GL_PROJECTION_MATRIX, &proj[0] );

   /* Get the current MODELVIEW matrix from OpenGL */
   glGetFloatv( GL_MODELVIEW_MATRIX, &modl[0] );

//   for(unsigned int i = 0; i < proj.size(); ++i) {
//	   //printf("\ni = %d proj [%f][%f] modl [%f][%f]\n",i,proj[i],quadCacheItem.proj[i],modl[i],quadCacheItem.modl[i]);
//	   if(proj[i] != quadCacheItem.proj[i]) {
//		   frustumChanged = true;
//		   break;
//	   }
//	   if(modl[i] != quadCacheItem.modl[i]) {
//		   frustumChanged = true;
//		   break;
//	   }
//   }

   // Check the frustum cache
   const bool useFrustumCache = Config::getInstance().getBool("EnableFrustrumCache","false");
   pair<vector<float>,vector<float> > lookupKey;
   if(useFrustumCache == true) {
	   lookupKey = make_pair(proj,modl);
	   map<pair<vector<float>,vector<float> >, vector<vector<float> > >::iterator iterFind = quadCacheItem.frustumDataCache.find(lookupKey);
	   if(iterFind != quadCacheItem.frustumDataCache.end()) {
		   if(SystemFlags::VERBOSE_MODE_ENABLED) printf("\nCalc Frustum found in cache\n");

		   quadCacheItem.frustumData = iterFind->second;
		   frustumChanged = (quadCacheItem.proj != proj || quadCacheItem.modl != modl);
		   if(frustumChanged == true) {
			   quadCacheItem.proj = proj;
			   quadCacheItem.modl = modl;
		   }

		   return frustumChanged;
	   }
   }

   if(quadCacheItem.proj != proj || quadCacheItem.modl != modl) {
   //if(frustumChanged == true) {
	   frustumChanged = true;
	   vector<vector<float> > &frustum = quadCacheItem.frustumData;
	   //assert(frustum.size() == 6);
	   //assert(frustum[0].size() == 4);

	   quadCacheItem.proj = proj;
	   quadCacheItem.modl = modl;

	   float   clip[16];
	   float   t=0;

	   /* Combine the two matrices (multiply projection by modelview) */
	   clip[ 0] = modl[ 0] * proj[ 0] + modl[ 1] * proj[ 4] + modl[ 2] * proj[ 8] + modl[ 3] * proj[12];
	   clip[ 1] = modl[ 0] * proj[ 1] + modl[ 1] * proj[ 5] + modl[ 2] * proj[ 9] + modl[ 3] * proj[13];
	   clip[ 2] = modl[ 0] * proj[ 2] + modl[ 1] * proj[ 6] + modl[ 2] * proj[10] + modl[ 3] * proj[14];
	   clip[ 3] = modl[ 0] * proj[ 3] + modl[ 1] * proj[ 7] + modl[ 2] * proj[11] + modl[ 3] * proj[15];

	   clip[ 4] = modl[ 4] * proj[ 0] + modl[ 5] * proj[ 4] + modl[ 6] * proj[ 8] + modl[ 7] * proj[12];
	   clip[ 5] = modl[ 4] * proj[ 1] + modl[ 5] * proj[ 5] + modl[ 6] * proj[ 9] + modl[ 7] * proj[13];
	   clip[ 6] = modl[ 4] * proj[ 2] + modl[ 5] * proj[ 6] + modl[ 6] * proj[10] + modl[ 7] * proj[14];
	   clip[ 7] = modl[ 4] * proj[ 3] + modl[ 5] * proj[ 7] + modl[ 6] * proj[11] + modl[ 7] * proj[15];

	   clip[ 8] = modl[ 8] * proj[ 0] + modl[ 9] * proj[ 4] + modl[10] * proj[ 8] + modl[11] * proj[12];
	   clip[ 9] = modl[ 8] * proj[ 1] + modl[ 9] * proj[ 5] + modl[10] * proj[ 9] + modl[11] * proj[13];
	   clip[10] = modl[ 8] * proj[ 2] + modl[ 9] * proj[ 6] + modl[10] * proj[10] + modl[11] * proj[14];
	   clip[11] = modl[ 8] * proj[ 3] + modl[ 9] * proj[ 7] + modl[10] * proj[11] + modl[11] * proj[15];

	   clip[12] = modl[12] * proj[ 0] + modl[13] * proj[ 4] + modl[14] * proj[ 8] + modl[15] * proj[12];
	   clip[13] = modl[12] * proj[ 1] + modl[13] * proj[ 5] + modl[14] * proj[ 9] + modl[15] * proj[13];
	   clip[14] = modl[12] * proj[ 2] + modl[13] * proj[ 6] + modl[14] * proj[10] + modl[15] * proj[14];
	   clip[15] = modl[12] * proj[ 3] + modl[13] * proj[ 7] + modl[14] * proj[11] + modl[15] * proj[15];

	   /* Extract the numbers for the RIGHT plane */
	   frustum[0][0] = clip[ 3] - clip[ 0];
	   frustum[0][1] = clip[ 7] - clip[ 4];
	   frustum[0][2] = clip[11] - clip[ 8];
	   frustum[0][3] = clip[15] - clip[12];

	   if(SystemFlags::VERBOSE_MODE_ENABLED) printf("\nCalc Frustum #%da: [%f][%f][%f][%f]\n",0,frustum[0][0],frustum[0][1],frustum[0][2],frustum[0][3]);

	   /* Normalize the result */
	   t = std::sqrt( frustum[0][0] * frustum[0][0] + frustum[0][1] * frustum[0][1] + frustum[0][2] * frustum[0][2] );
	   if(t != 0.0) {
		   frustum[0][0] /= t;
		   frustum[0][1] /= t;
		   frustum[0][2] /= t;
		   frustum[0][3] /= t;

		   if(SystemFlags::VERBOSE_MODE_ENABLED) printf("\nCalc Frustum #%db: [%f][%f][%f][%f] t = %f\n",0,frustum[0][0],frustum[0][1],frustum[0][2],frustum[0][3],t);
	   }

	   /* Extract the numbers for the LEFT plane */
	   frustum[1][0] = clip[ 3] + clip[ 0];
	   frustum[1][1] = clip[ 7] + clip[ 4];
	   frustum[1][2] = clip[11] + clip[ 8];
	   frustum[1][3] = clip[15] + clip[12];

	   if(SystemFlags::VERBOSE_MODE_ENABLED) printf("\nCalc Frustum #%da: [%f][%f][%f][%f]\n",1,frustum[1][0],frustum[1][1],frustum[1][2],frustum[1][3]);

	   /* Normalize the result */
	   t = std::sqrt( frustum[1][0] * frustum[1][0] + frustum[1][1] * frustum[1][1] + frustum[1][2] * frustum[1][2] );
	   if(t != 0.0) {
		   frustum[1][0] /= t;
		   frustum[1][1] /= t;
		   frustum[1][2] /= t;
		   frustum[1][3] /= t;

		   if(SystemFlags::VERBOSE_MODE_ENABLED) printf("\nCalc Frustum #%db: [%f][%f][%f][%f] t = %f\n",1,frustum[1][0],frustum[1][1],frustum[1][2],frustum[1][3],t);
	   }

	   /* Extract the BOTTOM plane */
	   frustum[2][0] = clip[ 3] + clip[ 1];
	   frustum[2][1] = clip[ 7] + clip[ 5];
	   frustum[2][2] = clip[11] + clip[ 9];
	   frustum[2][3] = clip[15] + clip[13];

	   if(SystemFlags::VERBOSE_MODE_ENABLED) printf("\nCalc Frustum #%da: [%f][%f][%f][%f]\n",2,frustum[2][0],frustum[2][1],frustum[2][2],frustum[2][3]);

	   /* Normalize the result */

	   t = std::sqrt( frustum[2][0] * frustum[2][0] + frustum[2][1] * frustum[2][1] + frustum[2][2] * frustum[2][2] );
	   if(t != 0.0) {
		   frustum[2][0] /= t;
		   frustum[2][1] /= t;
		   frustum[2][2] /= t;
		   frustum[2][3] /= t;

		   if(SystemFlags::VERBOSE_MODE_ENABLED) printf("\nCalc Frustum #%db: [%f][%f][%f][%f] t = %f\n",2,frustum[2][0],frustum[2][1],frustum[2][2],frustum[2][3],t);
	   }

	   /* Extract the TOP plane */
	   frustum[3][0] = clip[ 3] - clip[ 1];
	   frustum[3][1] = clip[ 7] - clip[ 5];
	   frustum[3][2] = clip[11] - clip[ 9];
	   frustum[3][3] = clip[15] - clip[13];

	   if(SystemFlags::VERBOSE_MODE_ENABLED) printf("\nCalc Frustum #%da: [%f][%f][%f][%f]\n",3,frustum[3][0],frustum[3][1],frustum[3][2],frustum[3][3]);

	   /* Normalize the result */

	   t = std::sqrt( frustum[3][0] * frustum[3][0] + frustum[3][1] * frustum[3][1] + frustum[3][2] * frustum[3][2] );
	   if(t != 0.0) {
		   frustum[3][0] /= t;
		   frustum[3][1] /= t;
		   frustum[3][2] /= t;
		   frustum[3][3] /= t;

		   if(SystemFlags::VERBOSE_MODE_ENABLED) printf("\nCalc Frustum #%db: [%f][%f][%f][%f] t = %f\n",3,frustum[3][0],frustum[3][1],frustum[3][2],frustum[3][3],t);
	   }

	   /* Extract the FAR plane */
	   frustum[4][0] = clip[ 3] - clip[ 2];
	   frustum[4][1] = clip[ 7] - clip[ 6];
	   frustum[4][2] = clip[11] - clip[10];
	   frustum[4][3] = clip[15] - clip[14];

	   if(SystemFlags::VERBOSE_MODE_ENABLED) printf("\nCalc Frustum #%da: [%f][%f][%f][%f]\n",4,frustum[4][0],frustum[4][1],frustum[4][2],frustum[4][3]);

	   /* Normalize the result */

	   t = std::sqrt( frustum[4][0] * frustum[4][0] + frustum[4][1] * frustum[4][1] + frustum[4][2] * frustum[4][2] );

	   if(t != 0.0) {
		   frustum[4][0] /= t;
		   frustum[4][1] /= t;
		   frustum[4][2] /= t;
		   frustum[4][3] /= t;

		   if(SystemFlags::VERBOSE_MODE_ENABLED) printf("\nCalc Frustum #%db: [%f][%f][%f][%f] t = %f\n",4,frustum[4][0],frustum[4][1],frustum[4][2],frustum[4][3],t);
	   }

	   /* Extract the NEAR plane */
	   frustum[5][0] = clip[ 3] + clip[ 2];
	   frustum[5][1] = clip[ 7] + clip[ 6];
	   frustum[5][2] = clip[11] + clip[10];
	   frustum[5][3] = clip[15] + clip[14];

	   if(SystemFlags::VERBOSE_MODE_ENABLED) printf("\nCalc Frustum #%da: [%f][%f][%f][%f]\n",5,frustum[5][0],frustum[5][1],frustum[5][2],frustum[5][3]);

	   /* Normalize the result */

	   t = std::sqrt( frustum[5][0] * frustum[5][0] + frustum[5][1] * frustum[5][1] + frustum[5][2] * frustum[5][2] );

	   if(t != 0.0) {
		   frustum[5][0] /= t;
		   frustum[5][1] /= t;
		   frustum[5][2] /= t;
		   frustum[5][3] /= t;

		   if(SystemFlags::VERBOSE_MODE_ENABLED) printf("\nCalc Frustum #%db: [%f][%f][%f][%f] t = %f\n",5,frustum[5][0],frustum[5][1],frustum[5][2],frustum[5][3],t);
	   }

	   if(useFrustumCache == true) {
		   quadCacheItem.frustumDataCache[lookupKey] = frustum;
	   }
   }
   return frustumChanged;
}

bool Renderer::PointInFrustum(vector<vector<float> > &frustum, float x, float y, float z ) {
   unsigned int p=0;

   for( p = 0; p < frustum.size(); p++ ) {
      if( frustum[p][0] * x + frustum[p][1] * y + frustum[p][2] * z + frustum[p][3] <= 0 ) {
         return false;
      }
   }
   return true;
}

bool Renderer::SphereInFrustum(vector<vector<float> > &frustum,  float x, float y, float z, float radius) {
	// Go through all the sides of the frustum
	for(int i = 0; i < (int)frustum.size(); i++ ) {
		// If the center of the sphere is farther away from the plane than the radius
		if(frustum[i][0] * x + frustum[i][1] * y + frustum[i][2] * z + frustum[i][3] <= -radius ) {
			// The distance was greater than the radius so the sphere is outside of the frustum
			return false;
		}
	}

	// The sphere was inside of the frustum!
	return true;
}

bool Renderer::CubeInFrustum(vector<vector<float> > &frustum, float x, float y, float z, float size ) {
   unsigned int p=0;

   for( p = 0; p < frustum.size(); p++ ) {
      if( frustum[p][0] * (x - size) + frustum[p][1] * (y - size) + frustum[p][2] * (z - size) + frustum[p][3] > 0 )
         continue;
      if( frustum[p][0] * (x + size) + frustum[p][1] * (y - size) + frustum[p][2] * (z - size) + frustum[p][3] > 0 )
         continue;
      if( frustum[p][0] * (x - size) + frustum[p][1] * (y + size) + frustum[p][2] * (z - size) + frustum[p][3] > 0 )
         continue;
      if( frustum[p][0] * (x + size) + frustum[p][1] * (y + size) + frustum[p][2] * (z - size) + frustum[p][3] > 0 )
         continue;
      if( frustum[p][0] * (x - size) + frustum[p][1] * (y - size) + frustum[p][2] * (z + size) + frustum[p][3] > 0 )
         continue;
      if( frustum[p][0] * (x + size) + frustum[p][1] * (y - size) + frustum[p][2] * (z + size) + frustum[p][3] > 0 )
         continue;
      if( frustum[p][0] * (x - size) + frustum[p][1] * (y + size) + frustum[p][2] * (z + size) + frustum[p][3] > 0 )
         continue;
      if( frustum[p][0] * (x + size) + frustum[p][1] * (y + size) + frustum[p][2] * (z + size) + frustum[p][3] > 0 )
         continue;
      return false;
   }
   return true;
}

void Renderer::computeVisibleQuad() {
	visibleQuad = this->gameCamera->computeVisibleQuad();

	bool frustumChanged = false;
	if(VisibleQuadContainerCache::enableFrustumCalcs == true) {
		frustumChanged = ExtractFrustum(quadCache);
	}

	if(frustumChanged && SystemFlags::VERBOSE_MODE_ENABLED) {
		printf("\nCamera: %d,%d %d,%d %d,%d %d,%d\n",
			visibleQuad.p[0].x,visibleQuad.p[0].y,
			visibleQuad.p[1].x,visibleQuad.p[1].y,
			visibleQuad.p[2].x,visibleQuad.p[2].y,
			visibleQuad.p[3].x,visibleQuad.p[3].y);

		for(unsigned int i = 0; i < quadCache.frustumData.size(); ++i) {
			printf("\nFrustum #%u [" MG_SIZE_T_SPECIFIER "]: ",i,quadCache.frustumData.size());
			vector<float> &frustumDataInner = quadCache.frustumData[i];
			for(unsigned int j = 0; j < frustumDataInner.size(); ++j) {
				printf("[%f]",quadCache.frustumData[i][j]);
			}
		}

		printf("\nEND\n");
	}

	const bool newVisibleQuadCalc = false;
	if(newVisibleQuadCalc) {
		const bool debug = false;
		try {
			if(debug) {
				visibleQuad = gameCamera->computeVisibleQuad();
				printf("Camera: %d,%d %d,%d %d,%d %d,%d\n",
					visibleQuad.p[0].x,visibleQuad.p[0].y,
					visibleQuad.p[1].x,visibleQuad.p[1].y,
					visibleQuad.p[2].x,visibleQuad.p[2].y,
					visibleQuad.p[3].x,visibleQuad.p[3].y);
			}


			// compute the four corners using OpenGL
			GLdouble model[16], projection[16];
			GLint viewport[4];
			glGetDoublev(GL_MODELVIEW_MATRIX,model);
			glGetDoublev(GL_PROJECTION_MATRIX,projection);
			glGetIntegerv(GL_VIEWPORT,viewport);
			Vec2i
				tl = _unprojectMap(Vec2i(0,0),model,projection,viewport,"tl"),
				tr = _unprojectMap(Vec2i(viewport[2],0),model,projection,viewport,"tr"),
				br = _unprojectMap(Vec2i(viewport[2],viewport[3]),model,projection,viewport,"br"),
				bl = _unprojectMap(Vec2i(0,viewport[3]),model,projection,viewport,"bl");


			// orientate it for map iterator
			//bool swapRequiredX = false;
			bool swapRequiredY = false;
			int const cellBuffer = 4;
			if((tl.x > tr.x) || (bl.x > br.x)) {
				if(debug) printf("Swap X???\n");

				//std::swap(tl,bl);
				//std::swap(tr,br);
				if(tl.x > tr.x) {
					if(debug) printf("Swap X1???\n");

					tr.x += cellBuffer;
					tl.x -= cellBuffer;

					std::swap(tl.x,tr.x);
					//swapRequiredX = true;
				}
				else {
					tl.x += cellBuffer;
					tr.x -= cellBuffer;
				}
				if(bl.x > br.x) {
					if(debug) printf("Swap X2???\n");

					bl.x += cellBuffer;
					br.x -= cellBuffer;

					std::swap(bl.x,br.x);
					//swapRequiredX = true;
				}
				else {
					br.x += cellBuffer;
					bl.x -= cellBuffer;
				}
			}

			if((tl.y > bl.y) || (tr.y > br.y)) {
				visibleQuad = this->gameCamera->computeVisibleQuad();

				if(debug) printf("Swap Y???\n");

				if(tl.y > bl.y) {
					if(debug) printf("Swap Y1???\n");

					tl.y += cellBuffer;
					bl.y -= cellBuffer;

					std::swap(tl.y,bl.y);
					swapRequiredY = true;
				}
				else {
					bl.y += cellBuffer;
					tl.y -= cellBuffer;
				}
				if(tr.y > br.y) {
					if(debug) printf("Swap Y2???\n");

					tr.y += cellBuffer;
					br.y -= cellBuffer;

					std::swap(tr.y,br.y);
					swapRequiredY = true;
				}
				else {
					br.y += cellBuffer;
					tr.y -= cellBuffer;
				}


				//std::swap(tl,tr);
				//std::swap(bl,br);
			}
			if(swapRequiredY == false) {
				tl.y -= cellBuffer;
				tr.y -= cellBuffer;
				bl.y += cellBuffer;
				br.y += cellBuffer;
			}

			// set it as the frustum
			visibleQuad = Quad2i(tl,bl,tr,br); // strange order
			if(debug) {
				printf("Will:   %d,%d %d,%d %d,%d %d,%d\n",
					visibleQuad.p[0].x,visibleQuad.p[0].y,
					visibleQuad.p[1].x,visibleQuad.p[1].y,
					visibleQuad.p[2].x,visibleQuad.p[2].y,
					visibleQuad.p[3].x,visibleQuad.p[3].y);
			}
		}
		catch(PROJECTION_TO_INFINITY &e) {
			if(debug) printf("hmm staring at the horizon %d\n",(int)e);
			// use historic code solution
			visibleQuad = this->gameCamera->computeVisibleQuad();
		}
	}
}

// =======================================
// basic rendering
// =======================================

void Renderer::renderMouse2d(int x, int y, int anim, float fade) {
	if(no2DMouseRendering == true) {
			return;
	}
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}
//	float blue=0.0f;
//	float green=0.4f;
	if(game != NULL && game->getGui() != NULL) {
		const Gui *gui=game->getGui();
		const Display *display=gui->getDisplay();
		int downPos= display->getDownSelectedPos();
		if(downPos != Display::invalidPos){
			// in state of doing something
			const Texture2D *texture= display->getDownImage(downPos);
			renderTextureQuad(x+18,y-50,32,32,texture,0.8f);
		}
//		else {
//			// Display current commandtype
//			const Unit *unit=NULL;
//			if(gui->getSelection()->isEmpty()){
//				blue=0.0f;
//				green=0.1f;
//			}
//			else{
//				unit=gui->getSelection()->getFrontUnit();
//				if(unit->getCurrCommand()!=NULL && unit->getCurrCommand()->getCommandType()->getImage()!=NULL){
//					const Texture2D *texture = unit->getCurrCommand()->getCommandType()->getImage();
//					renderTextureQuad(x+18,y-50,32,32,texture,0.2f);
//				}
//			}
//		}

		if(game->isMarkCellMode() == true) {
			const Texture2D *texture= game->getMarkCellTexture();
			renderTextureQuad(x,y,texture->getTextureWidth(),texture->getTextureHeight(),texture,0.8f);
		}
		if(game->isUnMarkCellMode() == true) {
			const Texture2D *texture= game->getUnMarkCellTexture();
			renderTextureQuad(x,y,texture->getTextureWidth(),texture->getTextureHeight(),texture,0.8f);
		}
	}

	float fadeFactor = fade + 1.f;

	anim= anim * 2 - maxMouse2dAnim;

	float color2= (abs(anim*(int)fadeFactor)/static_cast<float>(maxMouse2dAnim))/2.f+0.4f;
	float color1= (abs(anim*(int)fadeFactor)/static_cast<float>(maxMouse2dAnim))/2.f+0.8f;

	glPushAttrib(GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT | GL_LINE_BIT);
		glEnable(GL_BLEND);

		//inside
		glColor4f(0.4f*fadeFactor, 0.2f*fadeFactor, 0.2f*fadeFactor, 0.5f*fadeFactor);
		glBegin(GL_TRIANGLES);
			glVertex2i(x, y);
			glVertex2i(x+20, y-10);
			glVertex2i(x+10, y-20);
		glEnd();

		//border
		glLineWidth(2);
		glBegin(GL_LINE_LOOP);
			glColor4f(1.f, 0.2f, 0, color1);
			glVertex2i(x, y);
			glColor4f(1.f, 0.4f, 0, color2);
			glVertex2i(x+20, y-10);
			glColor4f(1.f, 0.4f, 0, color2);
			glVertex2i(x+10, y-20);
		glEnd();
	glPopAttrib();


/*
	if(no2DMouseRendering == true) {
		return;
	}
    float color1 = 0.0, color2 = 0.0;

	float fadeFactor = fade + 1.f;

	anim= anim * 2 - maxMouse2dAnim;

    color2= (abs(anim*(int)fadeFactor)/static_cast<float>(maxMouse2dAnim))/2.f+0.4f;
    color1= (abs(anim*(int)fadeFactor)/static_cast<float>(maxMouse2dAnim))/2.f+0.8f;

    glPushAttrib(GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT | GL_LINE_BIT);
    glEnable(GL_BLEND);

	//inside
	Vec2i vertices[3];
	vertices[0] = Vec2i(x, y);
	vertices[1] = Vec2i(x+20, y-10);
	vertices[2] = Vec2i(x+10, y-20);

	glColor4f(0.4f*fadeFactor, 0.2f*fadeFactor, 0.2f*fadeFactor, 0.5f*fadeFactor);
	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(2, GL_INT, 0, &vertices[0]);
	glDrawArrays(GL_TRIANGLES, 0, 3);
    glDisableClientState(GL_VERTEX_ARRAY);

	//border
	vertices[0] = Vec2i(x, y);
	vertices[1] = Vec2i(x+20, y-10);
	vertices[2] = Vec2i(x+10, y-20);

	Vec4f colors[4];
	colors[0] = Vec4f(1.f, 0.2f, 0, color1);
	colors[1] = Vec4f(1.f, 0.4f, 0, color2);
	colors[2] = Vec4f(1.f, 0.4f, 0, color2);

	glLineWidth(2);
	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(2, GL_INT, 0, &vertices[0]);
	glEnableClientState(GL_COLOR_ARRAY);
	glColorPointer(4, GL_FLOAT, 0, &colors[0]);
    glDrawArrays(GL_LINE_LOOP, 0, 3);
    glDisableClientState(GL_COLOR_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);

    glPopAttrib();
*/
}

void Renderer::renderMouse3d() {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	Config &config= Config::getInstance();
	if(config.getBool("RecordMode","false") == true) {
		return;
	}

	if(game == NULL) {
		char szBuf[8096]="";
		snprintf(szBuf,8096,"In [%s::%s] Line: %d game == NULL",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
		throw megaglest_runtime_error(szBuf);
	}
	else if(game->getGui() == NULL) {
		char szBuf[8096]="";
		snprintf(szBuf,8096,"In [%s::%s] Line: %d game->getGui() == NULL",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
		throw megaglest_runtime_error(szBuf);
	}
	else if(game->getGui()->getMouse3d() == NULL) {
		char szBuf[8096]="";
		snprintf(szBuf,8096,"In [%s::%s] Line: %d game->getGui()->getMouse3d() == NULL",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
		throw megaglest_runtime_error(szBuf);
	}

	const Gui *gui= game->getGui();
	const Mouse3d *mouse3d= gui->getMouse3d();
	const Map *map= game->getWorld()->getMap();
	if(map == NULL) {
		char szBuf[8096]="";
		snprintf(szBuf,8096,"In [%s::%s] Line: %d map == NULL",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
		throw megaglest_runtime_error(szBuf);
	}

	assertGl();

	if((mouse3d->isEnabled() || gui->isPlacingBuilding()) && gui->isValidPosObjWorld()) {
		const Vec2i &pos= gui->getPosObjWorld();

		glMatrixMode(GL_MODELVIEW);

		glPushAttrib(GL_CURRENT_BIT | GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT);
		glEnable(GL_BLEND);
		glDisable(GL_STENCIL_TEST);
		glDepthFunc(GL_LESS);
		glEnable(GL_COLOR_MATERIAL);
		glDepthMask(GL_FALSE);

		if(gui->isPlacingBuilding()) {

			modelRenderer->begin(true, true, false, false);

			const UnitType *building= gui->getBuilding();
			const Gui *gui= game->getGui();
			renderGhostModel(building, pos, gui->getSelectedFacing());

			modelRenderer->end();

			glDisable(GL_COLOR_MATERIAL);
			glPopAttrib();
		}
		else {
			glPushMatrix();
			Vec3f pos3f= Vec3f(pos.x, map->getCell(pos)->getHeight(), pos.y);
			Vec4f color;
			GLUquadricObj *cilQuadric;
			//standard mouse
			glDisable(GL_TEXTURE_2D);
			glDisable(GL_CULL_FACE);
			color= Vec4f(1.f, 0.f, 0.f, 1.f-mouse3d->getFade());
			glColor4fv(color.ptr());
			glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color.ptr());

			glTranslatef(pos3f.x, pos3f.y+2.f, pos3f.z);
			glRotatef(90.f, 1.f, 0.f, 0.f);
			glRotatef(static_cast<float>(mouse3d->getRot()), 0.f, 0.f, 1.f);

			cilQuadric= gluNewQuadric();
			gluQuadricDrawStyle(cilQuadric, GLU_FILL);
			gluCylinder(cilQuadric, 0.5f, 0.f, 2.f, 4, 1);
			gluCylinder(cilQuadric, 0.5f, 0.f, 0.f, 4, 1);
			glTranslatef(0.f, 0.f, 1.f);
			gluCylinder(cilQuadric, 0.7f, 0.f, 1.f, 4, 1);
			gluCylinder(cilQuadric, 0.7f, 0.f, 0.f, 4, 1);
			gluDeleteQuadric(cilQuadric);

			glPopAttrib();
			glPopMatrix();
		}
	}

}

void Renderer::renderBackground(const Texture2D *texture) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	const Metrics &metrics= Metrics::getInstance();

	assertGl();

    glPushAttrib(GL_ENABLE_BIT);

	glDisable(GL_LIGHTING);
	glEnable(GL_TEXTURE_2D);

	renderQuad(0, 0, metrics.getVirtualW(), metrics.getVirtualH(), texture);

	glPopAttrib();

	assertGl();
}

void Renderer::renderTextureQuad(int x, int y, int w, int h, const Texture2D *texture, float alpha,const Vec3f *color) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

    assertGl();

	glPushAttrib(GL_ENABLE_BIT);

	glDisable(GL_LIGHTING);
	glEnable(GL_TEXTURE_2D);
	glEnable(GL_BLEND);

	if(color != NULL) {
		Vec4f newColor(*color);
		newColor.w = alpha;
		glColor4fv(newColor.ptr());
	}
	else {
		glColor4f(1.f, 1.f, 1.f, alpha);
	}
	renderQuad(x, y, w, h, texture);

	glPopAttrib();

	assertGl();
}

void Renderer::renderConsoleLine3D(int lineIndex, int xPosition, int yPosition, int lineHeight,
								Font3D* font, string stringToHightlight, const ConsoleLineInfo *lineInfo) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	Vec4f fontColor;
	Lang &lang= Lang::getInstance();
	//const Metrics &metrics= Metrics::getInstance();
	FontMetrics *fontMetrics= font->getMetrics();

	if(game != NULL) {
		fontColor = game->getGui()->getDisplay()->getColor();
	}
	else {
		// white shadowed is default ( in the menu for example )
		//fontColor=Vec4f(1.f, 1.f, 1.f, 0.0f);
		fontColor=Vec4f(lineInfo->color.x,lineInfo->color.y,lineInfo->color.z, 0.0f);
	}

	Vec4f defaultFontColor = fontColor;

	if(lineInfo->PlayerIndex >= 0) {
		std::map<int,Texture2D *> &crcPlayerTextureCache = CacheManager::getCachedItem< std::map<int,Texture2D *> >(GameConstants::playerTextureCacheLookupKey);
		Vec3f playerColor = crcPlayerTextureCache[lineInfo->PlayerIndex]->getPixmap()->getPixel3f(0, 0);
		fontColor.x = playerColor.x;
		fontColor.y = playerColor.y;
		fontColor.z = playerColor.z;

		GameNetworkInterface *gameNetInterface = NetworkManager::getInstance().getGameNetworkInterface();
		if(gameNetInterface != NULL && gameNetInterface->getGameSettings() != NULL) {
			const GameSettings *gameSettings = gameNetInterface->getGameSettings();
			string playerName = gameSettings->getNetworkPlayerNameByPlayerIndex(lineInfo->PlayerIndex);
			if(playerName != lineInfo->originalPlayerName && lineInfo->originalPlayerName != "") {
				playerName = lineInfo->originalPlayerName;
			}
			if(playerName == GameConstants::NETWORK_SLOT_UNCONNECTED_SLOTNAME) {
				playerName = lang.getString("SystemUser");
			}
			//printf("playerName [%s], line [%s]\n",playerName.c_str(),line.c_str());

			//string headerLine = "*" + playerName + ":";
			//string headerLine = playerName + ": ";
			string headerLine = playerName;
			if(lineInfo->teamMode == true) {
				headerLine += " (" + lang.getString("Team") + ")";
			}
			headerLine += ": ";

			if(fontMetrics == NULL) {
				throw megaglest_runtime_error("fontMetrics == NULL");
			}

			renderTextShadow3D(
					headerLine,
					font,
					fontColor,
                	xPosition, lineIndex * lineHeight + yPosition);

			fontColor = defaultFontColor;
			//xPosition += (8 * (playerName.length() + 2));
			// Proper font spacing after username portion of chat text rendering

			//xPosition += (metrics.toVirtualX(fontMetrics->getTextWidth(headerLine)));
			xPosition += fontMetrics->getTextWidth(headerLine);
		}
	}
	else if(lineInfo->originalPlayerName != "") {
        string playerName = lineInfo->originalPlayerName;
        //string headerLine = playerName + ": ";
		string headerLine = playerName;
		if(lineInfo->teamMode == true) {
			headerLine += " (" + lang.getString("Team") + ")";
		}
		headerLine += ": ";

        if(fontMetrics == NULL) {
            throw megaglest_runtime_error("fontMetrics == NULL");
        }

        renderTextShadow3D(
                headerLine,
                font,
                fontColor,
                xPosition, lineIndex * lineHeight + yPosition);

        fontColor = defaultFontColor;
        //xPosition += (8 * (playerName.length() + 2));
        // Proper font spacing after username portion of chat text rendering
        //xPosition += (metrics.toVirtualX(fontMetrics->getTextWidth(headerLine)));
        xPosition += fontMetrics->getTextWidth(headerLine);
	}
	else {
		fontColor = defaultFontColor;
	}

	if(stringToHightlight!="" && lineInfo->text.find(stringToHightlight)!=string::npos){
		fontColor=Vec4f(1.f, 0.5f, 0.5f, 0.0f);
	}
	renderTextShadow3D(
			lineInfo->text,
		font,
		fontColor,
        xPosition, (lineIndex * lineHeight) + yPosition);
}

void Renderer::renderConsoleLine(int lineIndex, int xPosition, int yPosition, int lineHeight,
								Font2D* font, string stringToHightlight, const ConsoleLineInfo *lineInfo) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	Vec4f fontColor;
	Lang &lang= Lang::getInstance();

	const Metrics &metrics= Metrics::getInstance();
	FontMetrics *fontMetrics= font->getMetrics();

	if(game != NULL) {
		fontColor = game->getGui()->getDisplay()->getColor();
	}
	else {
		// white shadowed is default ( in the menu for example )
		//fontColor=Vec4f(1.f, 1.f, 1.f, 0.0f);
		fontColor=Vec4f(lineInfo->color.x,lineInfo->color.y,lineInfo->color.z, 0.0f);
	}

	Vec4f defaultFontColor = fontColor;

	if(lineInfo->PlayerIndex >= 0) {
		std::map<int,Texture2D *> &crcPlayerTextureCache = CacheManager::getCachedItem< std::map<int,Texture2D *> >(GameConstants::playerTextureCacheLookupKey);
		Vec3f playerColor = crcPlayerTextureCache[lineInfo->PlayerIndex]->getPixmap()->getPixel3f(0, 0);
		fontColor.x = playerColor.x;
		fontColor.y = playerColor.y;
		fontColor.z = playerColor.z;

		GameNetworkInterface *gameNetInterface = NetworkManager::getInstance().getGameNetworkInterface();
		if(gameNetInterface != NULL && gameNetInterface->getGameSettings() != NULL) {
			const GameSettings *gameSettings = gameNetInterface->getGameSettings();
			string playerName = gameSettings->getNetworkPlayerNameByPlayerIndex(lineInfo->PlayerIndex);
			if(playerName != lineInfo->originalPlayerName && lineInfo->originalPlayerName != "") {
				playerName = lineInfo->originalPlayerName;
			}
			//printf("playerName [%s], line [%s]\n",playerName.c_str(),line.c_str());

			//string headerLine = "*" + playerName + ":";
			//string headerLine = playerName + ": ";
			string headerLine = playerName;
			if(lineInfo->teamMode == true) {
				headerLine += " (" + lang.getString("Team") + ")";
			}
			headerLine += ": ";

			if(fontMetrics == NULL) {
				throw megaglest_runtime_error("fontMetrics == NULL");
			}

			renderTextShadow(
					headerLine,
					font,
					fontColor,
                	xPosition, lineIndex * lineHeight + yPosition);

			fontColor = defaultFontColor;
			//xPosition += (8 * (playerName.length() + 2));
			// Proper font spacing after username portion of chat text rendering
			xPosition += (metrics.toVirtualX(fontMetrics->getTextWidth(headerLine)));
		}
	}
	else if(lineInfo->originalPlayerName != "") {
        string playerName = lineInfo->originalPlayerName;
        //string headerLine = playerName + ": ";
        string headerLine = playerName;
		if(lineInfo->teamMode == true) {
			headerLine += " (" + lang.getString("Team") + ")";
		}
		headerLine += ": ";

        if(fontMetrics == NULL) {
            throw megaglest_runtime_error("fontMetrics == NULL");
        }

        renderTextShadow(
                headerLine,
                font,
                fontColor,
                xPosition, lineIndex * lineHeight + yPosition);

        fontColor = defaultFontColor;
        //xPosition += (8 * (playerName.length() + 2));
        // Proper font spacing after username portion of chat text rendering
        xPosition += (metrics.toVirtualX(fontMetrics->getTextWidth(headerLine)));
	}
	else {
		fontColor = defaultFontColor;
	}

	if(stringToHightlight!="" && lineInfo->text.find(stringToHightlight)!=string::npos){
		fontColor=Vec4f(1.f, 0.5f, 0.5f, 0.0f);
	}
	renderTextShadow(
			lineInfo->text,
		font,
		fontColor,
        xPosition, (lineIndex * lineHeight) + yPosition);
}

void Renderer::renderConsole(const Console *console, ConsoleMode mode , int overrideMaxConsoleLines){
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	if(console == NULL) {
		throw megaglest_runtime_error("console == NULL");
	}

	glPushAttrib(GL_ENABLE_BIT);
	glEnable(GL_BLEND);

	if(mode==consoleFull) {
	    int x= console->getXPos()-5;
	    int y= console->getYPos()-5;
	    int h= console->getLineHeight()*console->getStoredLineCount();

	    if(h > 0) {
	    	int w= 1000;
	    	//background
	    	glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT);
	    	glEnable(GL_BLEND);

	    	glColor4f(0.0f, 0.0f, 0.0f, 0.8f) ;

	    	glBegin(GL_TRIANGLE_STRIP);
	    		glVertex2i(x, y);
	    		glVertex2i(x, y+h);
	    		glVertex2i(x+w, y);
	    		glVertex2i(x+w, y+h);
	    	glEnd();
	    	glPopAttrib();
	    }
		for(int i = 0; i < console->getStoredLineCount(); ++i) {
			const ConsoleLineInfo &lineInfo = console->getStoredLineItem(i);
			if(renderText3DEnabled == true) {
				renderConsoleLine3D(i, console->getXPos(), console->getYPos(),
						console->getLineHeight(), console->getFont3D(),
						console->getStringToHighlight(), &lineInfo);
			}
			else {
				renderConsoleLine(i, console->getXPos(), console->getYPos(),
						console->getLineHeight(), console->getFont(),
						console->getStringToHighlight(), &lineInfo);
			}
		}
	}
	else if(mode==consoleStoredOnly) {
		int allowedMaxLines = (overrideMaxConsoleLines >= 0 ? overrideMaxConsoleLines : maxConsoleLines);
		for(int i = 0; i < console->getStoredLineCount() && i < allowedMaxLines; ++i) {
			const ConsoleLineInfo &lineInfo = console->getStoredLineItem(i);
			if(renderText3DEnabled == true) {
				renderConsoleLine3D(i, console->getXPos(), console->getYPos(),
						console->getLineHeight(), console->getFont3D(), console->getStringToHighlight(), &lineInfo);
			}
			else {
				renderConsoleLine(i, console->getXPos(), console->getYPos(),
						console->getLineHeight(), console->getFont(), console->getStringToHighlight(), &lineInfo);
			}
		}
	}
	else if(mode==consoleStoredAndNormal) {
		int allowedMaxLines = (overrideMaxConsoleLines >= 0 ? overrideMaxConsoleLines : maxConsoleLines);
		float starttimestamp=-1;
		int consoleIndex=0;
		for(int i = 0; i < console->getLineCount() && i < allowedMaxLines; ++i) {
			const ConsoleLineInfo &lineInfo = console->getLineItem(i);
			if(starttimestamp>lineInfo.timeStamp || starttimestamp==-1) starttimestamp=lineInfo.timeStamp;
			if(renderText3DEnabled == true) {
				renderConsoleLine3D(i, console->getXPos(), console->getYPos(),
						console->getLineHeight(), console->getFont3D(), console->getStringToHighlight(), &lineInfo);
			}
			else {
				renderConsoleLine(i, console->getXPos(), console->getYPos(),
						console->getLineHeight(), console->getFont(), console->getStringToHighlight(), &lineInfo);
			}
			consoleIndex++;
		}
		for(int i = 0; i < console->getStoredLineCount() && consoleIndex < allowedMaxLines; ++i) {
			const ConsoleLineInfo &lineInfo = console->getStoredLineItem(i);
			if( lineInfo.timeStamp<starttimestamp || starttimestamp==-1){
				if(renderText3DEnabled == true) {
					renderConsoleLine3D(consoleIndex, console->getXPos(), console->getYPos(),
							console->getLineHeight(), console->getFont3D(), console->getStringToHighlight(), &lineInfo);
				}
				else {
					renderConsoleLine(consoleIndex, console->getXPos(), console->getYPos(),
							console->getLineHeight(), console->getFont(), console->getStringToHighlight(), &lineInfo);
				}
				consoleIndex++;
			}
		}
	}
	else if(mode==consoleNormal) {
		for(int i = 0; i < console->getLineCount(); ++i) {
			const ConsoleLineInfo &lineInfo = console->getLineItem(i);
			if(renderText3DEnabled == true) {
				renderConsoleLine3D(i, console->getXPos(), console->getYPos(),
						console->getLineHeight(), console->getFont3D(), console->getStringToHighlight(), &lineInfo);
			}
			else {
				renderConsoleLine(i, console->getXPos(), console->getYPos(),
						console->getLineHeight(), console->getFont(), console->getStringToHighlight(), &lineInfo);
			}
		}
	}
	glPopAttrib();
}

void Renderer::renderChatManager(const ChatManager *chatManager) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	Vec4f fontColor;
	Lang &lang= Lang::getInstance();

	if(chatManager->getEditEnabled()) {
		Vec4f color=Vec4f(0.0f,0.0f,0.0f,0.6f);
		string text="";

		if(chatManager->isInCustomInputMode() == true) {
			text += lang.getString("CellHint");
		}
		else if(chatManager->getInMenu()) {
			text += lang.getString("Chat");
		}
		else if(chatManager->getTeamMode()) {
			text += lang.getString("Team");
		}
		else {
			text += lang.getString("All");
		}
		text += ": " + chatManager->getText() + "_";

		if(game != NULL) {
			fontColor = game->getGui()->getDisplay()->getColor();
		}
		else {
			// white shadowed is default ( in the menu for example )
			fontColor=Vec4f(1.f, 1.f, 1.f, 0.0f);
		}

		// render Background
		int x=chatManager->getXPos();
		int y=chatManager->getYPos()-6;
		int h=22;
		int w=830;

    	glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT);
    	glEnable(GL_BLEND);

    	glColor4f(color.x, color.y, color.z, color.w) ;

    	glBegin(GL_TRIANGLE_STRIP);
    		glVertex2i(x, y);
    		glVertex2i(x, y+h);
    		glVertex2i(x+w, y);
    		glVertex2i(x+w, y+h);
    	glEnd();
    	glPopAttrib();

		if(renderText3DEnabled == true) {
			renderTextShadow3D(
					text,
					chatManager->getFont3D(),
					fontColor,
					chatManager->getXPos(), chatManager->getYPos());
		}
		else {
			renderTextShadow(
					text,
					chatManager->getFont(),
					fontColor,
					chatManager->getXPos(), chatManager->getYPos());
		}
	}
	else
	{
		if (chatManager->getInMenu()) {
			string text = "\t\t\t\t\t>> "+lang.getString("PressEnterToChat")+" <<";
			fontColor = Vec4f(0.5f, 0.5f, 0.5f, 0.5f);

			if(renderText3DEnabled == true) {
				renderTextShadow3D(text, chatManager->getFont3D(), fontColor,
						chatManager->getXPos(), chatManager->getYPos());
			}
			else {
				renderTextShadow(text, chatManager->getFont(), fontColor,
						chatManager->getXPos(), chatManager->getYPos());
			}
		}
	}
}


void Renderer::renderPerformanceStats() {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	const Metrics &metrics = Metrics::getInstance();
	const Vec4f fontColor = game->getGui()->getDisplay()->getColor();

	char szBuf[200]="";
	snprintf(szBuf,200,"Frame: %d",game->getWorld()->getFrameCount() / 20);
	string str = string(szBuf) + string("\n");

	static time_t lastGamePerfCheck = time(NULL);
	static string gamePerfStats = "";
	if(difftime((long int)time(NULL),lastGamePerfCheck) > 3) {
		lastGamePerfCheck = time(NULL);
		gamePerfStats = game->getGamePerformanceCounts(true);
	}

	if(gamePerfStats != "") {
		str += gamePerfStats + "\n";
	}

	if(renderText3DEnabled == true) {
		renderTextShadow3D(
			str, CoreData::getInstance().getDisplayFontSmall3D(),
			fontColor,
			10, metrics.getVirtualH()-180, false);
	}
	else {
		renderTextShadow(
			str, CoreData::getInstance().getDisplayFontSmall(),
			fontColor,
			10, metrics.getVirtualH()-180, false);
	}
}

void Renderer::renderClock() {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	Config &config= Config::getInstance();
	if(config.getBool("InGameClock","true") == false &&
		config.getBool("InGameLocalClock","true") == false &&
		config.getBool("InGameFrameCounter","false") == false) {
		return;
	}

	string str = "";
	const Metrics &metrics = Metrics::getInstance();
	const World *world = game->getWorld();
	const Vec4f fontColor = game->getGui()->getDisplay()->getColor();

	if(config.getBool("InGameClock","true") == true) {
		Lang &lang= Lang::getInstance();
		char szBuf[501]="";

		//int hours = world->getTimeFlow()->getTime();
		//int minutes = (world->getTimeFlow()->getTime() - hours) * 100 * 0.6; // scale 100 to 60
		//snprintf(szBuf,200,"%s %.2d:%.2d",lang.getString("GameTime","",true).c_str(),hours,minutes);
		// string header2 = lang.getString("GameDurationTime","",true) + ": " + getTimeString(stats.getFramesToCalculatePlaytime());
		snprintf(szBuf,500,"%s %s",lang.getString("GameDurationTime","",true).c_str(),getTimeDuationString(world->getFrameCount(),GameConstants::updateFps).c_str());
		if(str != "") {
			str += " ";
		}
		str += szBuf;
	}

	if(config.getBool("InGameLocalClock","true") == true) {
		//time_t nowTime = time(NULL);
		//struct tm *loctime = localtime(&nowTime);
		struct tm loctime = threadsafe_localtime(systemtime_now());
		char szBuf2[100]="";
		strftime(szBuf2,100,"%H:%M",&loctime);

		Lang &lang= Lang::getInstance();
		char szBuf[200]="";
		snprintf(szBuf,200,"%s %s",lang.getString("LocalTime","",true).c_str(),szBuf2);
		if(str != "") {
			str += " ";
		}
		str += szBuf;
	}

	if(config.getBool("InGameFrameCounter","false") == true) {
		char szBuf[200]="";
		snprintf(szBuf,200,"Frame: %d",game->getWorld()->getFrameCount() / 20);
		if(str != "") {
			str += " ";
		}
		str += szBuf;
	}

	//string str = szBuf;

	if(renderText3DEnabled == true) {
		renderTextShadow3D(
			str, CoreData::getInstance().getDisplayFontSmall3D(),
			fontColor,
			10, metrics.getVirtualH()-160, false);
	}
	else {
		renderTextShadow(
			str, CoreData::getInstance().getDisplayFontSmall(),
			fontColor,
			10, metrics.getVirtualH()-160, false);
	}
}

void Renderer::renderResourceStatus() {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	const World *world		= game->getWorld();
	Config &config= Config::getInstance();

	if(world->getThisFactionIndex() < 0 ||
		world->getThisFactionIndex() >= world->getFactionCount()) {
		return;
	}

	const Faction *thisFaction = world->getFaction(world->getThisFactionIndex());

	assertGl();
	glPushAttrib(GL_ENABLE_BIT);

	int rowsRendered = 0;
	int resourceCountRendered = 0;
	bool twoRessourceLines=false;

	bool sharedTeamUnits = game != NULL && game->getGui() != NULL
			&& game->isFlagType1BitEnabled(ft1_allow_shared_team_units)
					== true;
	bool sharedTeamResources = game != NULL && game->getGui() != NULL
			&& game->isFlagType1BitEnabled(
					ft1_allow_shared_team_resources) == true;

	bool renderSharedTeamResources=false;
	bool renderSharedTeamUnits=false;
	bool renderLocalFactionResources=false;

	if(config.getBool("TwoLineTeamResourceRendering","false") == true) {
		if( sharedTeamResources == true || sharedTeamUnits == true){
			twoRessourceLines=true;
		}
		if(sharedTeamResources == true){
			renderSharedTeamResources=true;
			renderSharedTeamUnits=true;
		}
		else if(sharedTeamUnits == true){
			renderSharedTeamUnits=true;
			renderLocalFactionResources=true;
		}
		else{
			renderLocalFactionResources=true;
		}
	}
	else {
		if(sharedTeamResources == true)
			renderSharedTeamResources=true;
		else if(sharedTeamUnits == true)
			renderSharedTeamUnits=true;
		else
			renderLocalFactionResources=true;
	}

	if(renderSharedTeamResources == true) {
		resourceCountRendered = 0;
		for(int techTreeResourceTypeIndex = 0;
				techTreeResourceTypeIndex < world->getTechTree()->getResourceTypeCount();
					++techTreeResourceTypeIndex) {

			const ResourceType *rt 	= world->getTechTree()->getResourceType(techTreeResourceTypeIndex);

			if ( rt->getDisplayInHud() == false ) {
				continue;
			}

			bool showResource = world->showResourceTypeForTeam(rt, thisFaction->getTeam());
			if(showResource == true) {
				rowsRendered = renderResource(thisFaction,
						false, twoRessourceLines, rt, 0,
						resourceCountRendered);
			}
		}
		if(resourceCountRendered > 0) {
			rowsRendered++;
		}
	}

	if(renderLocalFactionResources == true){
		resourceCountRendered = 0;

		const Faction *factionForResourceView 	= thisFaction;
		bool localFactionResourcesOnly 			= true;

		for(int techTreeResourceTypeIndex = 0;
				techTreeResourceTypeIndex < world->getTechTree()->getResourceTypeCount();
					++techTreeResourceTypeIndex) {
			const ResourceType *rt 	= world->getTechTree()->getResourceType(techTreeResourceTypeIndex);
			if ( rt->getDisplayInHud() == false ) {
				continue;
			}

			//if any unit produces the resource
			bool showResource;
			if (twoRessourceLines)
				showResource = world->showResourceTypeForTeam(rt,
						factionForResourceView->getTeam());
			else
				showResource = world->showResourceTypeForFaction(rt,
						factionForResourceView);
			if(showResource == true) {
				renderResource(factionForResourceView, localFactionResourcesOnly,
						twoRessourceLines, rt, rowsRendered, resourceCountRendered);
			}
		}
		if(resourceCountRendered > 0) {
			rowsRendered++;
		}
	}

	if(renderSharedTeamUnits == true){
		resourceCountRendered = 0;

		const Faction *factionForResourceView 	= thisFaction;
		bool localFactionResourcesOnly 			= true;

		const Gui *gui = game->getGui();
		if(gui != NULL) {
			const Selection *selection = gui->getSelection();
			if(selection != NULL && selection->getCount() > 0 && selection->getFrontUnit() != NULL) {
				const Unit *selectedUnit = selection->getFrontUnit();
				if(selectedUnit != NULL && selectedUnit->getFaction()->isAlly(thisFaction) == true) {
					factionForResourceView	  = selectedUnit->getFaction();
				}
			}
		}

		for(int techTreeResourceTypeIndex = 0;
				techTreeResourceTypeIndex < world->getTechTree()->getResourceTypeCount();
					++techTreeResourceTypeIndex) {
			const ResourceType *rt 	= world->getTechTree()->getResourceType(techTreeResourceTypeIndex);
			if ( rt->getDisplayInHud() == false ) {
				continue;
			}

			//if any unit produces the resource
			bool showResource;
			if (twoRessourceLines)
				showResource = world->showResourceTypeForTeam(rt,
						factionForResourceView->getTeam());
			else
				showResource = world->showResourceTypeForFaction(rt,
						factionForResourceView);

			if(showResource == true) {
				renderResource(factionForResourceView, localFactionResourcesOnly,
						twoRessourceLines, rt, rowsRendered, resourceCountRendered);
			}
		}
		if(resourceCountRendered > 0) {
			rowsRendered++;
		}
	}

	glPopAttrib();

	assertGl();
}

int Renderer::renderResource(const Faction *factionForResourceView,bool localFactionResourcesOnly,
		bool twoResourceLines, const ResourceType *rt, int startRow, int &resourceCountRendered) {

	const Metrics &metrics	= Metrics::getInstance();
	const int MAX_RESOURCES_PER_ROW = 6;

	int resourceRowHeigth=30;
	int resourceYStart=metrics.getVirtualH()-30;
	if(twoResourceLines){
		// we need to save some space
		resourceYStart=metrics.getVirtualH()-22;
		resourceRowHeigth=16;
	}

	//draw resource status
	if(localFactionResourcesOnly == true) {
		Vec4f resourceFontColor = Vec4f(factionForResourceView->getTexture()->getPixmapConst()->getPixel3f(0,0));
		int resourceCol = 0;
		int resourceRow = startRow;

		int x=resourceCol * 100 + 190;
		int y=resourceYStart - (resourceRowHeigth * resourceRow);
		int h=16;
		int w=8;
		glColor3f(resourceFontColor.x,resourceFontColor.y,resourceFontColor.z);
		glBegin(GL_TRIANGLE_STRIP);
			glVertex2i(x, y+h);
			glVertex2i(x, y);
			glVertex2i(x+w, y+h/2);
		glEnd();
	}

	const Resource *r = factionForResourceView->getResource(rt,localFactionResourcesOnly);
	string str = intToStr(r->getAmount());

	glEnable(GL_TEXTURE_2D);

	const Vec4f fontColor 	= game->getGui()->getDisplay()->getColor();
	Vec4f resourceFontColor = fontColor;

	bool isNegativeConsumableDisplayCycle = false;
	if(rt->getClass() == rcConsumable) {
		// Show in yellow/orange/red font if negative
		if(r->getBalance() * 5 + r->getAmount() < 0) {
			if(time(NULL) % 2 == 0) {

				isNegativeConsumableDisplayCycle = true;
				if(r->getBalance() * 1 + r->getAmount() < 0) {
					glColor3f(RED.x,RED.y,RED.z);
					resourceFontColor = RED;
				}
				else if(r->getBalance() * 3 + r->getAmount() < 0) {
					glColor3f(ORANGE.x,ORANGE.y,ORANGE.z);
					resourceFontColor = ORANGE;
				}
				else if(r->getBalance() * 5 + r->getAmount() < 0) {
					glColor3f(YELLOW.x,YELLOW.y,YELLOW.z);
					resourceFontColor = YELLOW;
				}
			}
		}
	}

	if(isNegativeConsumableDisplayCycle == false) {
		glColor3f(1.f, 1.f, 1.f);
	}

	int resourceRow = startRow + (resourceCountRendered > 0 ? resourceCountRendered / MAX_RESOURCES_PER_ROW : 0);
	int resourceCol = resourceCountRendered % MAX_RESOURCES_PER_ROW;

	renderQuad(resourceCol * 100 + 200, resourceYStart - (resourceRowHeigth * resourceRow), 16, 16, rt->getImage());

	if(rt->getClass() != rcStatic) {
		str+= "/" + intToStr(factionForResourceView->getStoreAmount(rt,localFactionResourcesOnly));
	}
	if(rt->getClass() == rcConsumable) {
		str+= "(";
		if(r->getBalance() > 0) {
			str+= "+";
		}
		str+= intToStr(r->getBalance()) + ")";
	}

	glDisable(GL_TEXTURE_2D);

	if(renderText3DEnabled == true) {
		renderTextShadow3D(
			str, CoreData::getInstance().getDisplayFontSmall3D(),
			resourceFontColor,
			resourceCol * 100 + 220, resourceYStart - (resourceRowHeigth * resourceRow), false);
	}
	else {
		renderTextShadow(
			str, CoreData::getInstance().getDisplayFontSmall(),
			resourceFontColor,
			resourceCol * 100 + 220, resourceYStart - (resourceRowHeigth * resourceRow), false);
	}
	++resourceCountRendered;

	return resourceRow;
}

void Renderer::renderSelectionQuad() {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	Config &config= Config::getInstance();
	if(config.getBool("RecordMode","false") == true) {
		return;
	}

	const Gui *gui= game->getGui();
	const SelectionQuad *sq= gui->getSelectionQuad();

    Vec2i down= sq->getPosDown();
    Vec2i up= sq->getPosUp();

    if(gui->isSelecting()) {
        glPushAttrib(GL_CURRENT_BIT | GL_LINE_BIT);

    	Vec2i vertices[4];
    	vertices[0] = Vec2i(down.x, down.y);
    	vertices[1] = Vec2i(up.x, down.y);
    	vertices[2] = Vec2i(up.x, up.y);
    	vertices[3] = Vec2i(down.x, up.y);

    	glColor3f(0,1,0);
    	glEnableClientState(GL_VERTEX_ARRAY);
    	glVertexPointer(2, GL_INT, 0, &vertices[0]);
        glDrawArrays(GL_LINE_LOOP, 0, 4);
        glDisableClientState(GL_VERTEX_ARRAY);

/*
            glColor3f(0,1,0);
            glBegin(GL_LINE_LOOP);
                glVertex2i(down.x, down.y);
                glVertex2i(up.x, down.y);
                glVertex2i(up.x, up.y);
                glVertex2i(down.x, up.y);
            glEnd();
*/
        glPopAttrib();
    }
}

Vec2i computeCenteredPos(const string &text, Font2D *font, int x, int y) {
	if(font == NULL) {
		//abort();
		throw megaglest_runtime_error("font == NULL (1) text = " + text);
	}
	const Metrics &metrics= Metrics::getInstance();
	FontMetrics *fontMetrics= font->getMetrics();

	if(fontMetrics == NULL) {
		throw megaglest_runtime_error("fontMetrics == NULL (1) text = " + text);
	}

	int virtualX = (fontMetrics->getTextWidth(text) > 0 ? static_cast<int>(fontMetrics->getTextWidth(text)/2.f) : 5);
	int virtualY = (fontMetrics->getHeight(text) > 0 ? static_cast<int>(fontMetrics->getHeight(text)/2.f) : 5);

	Vec2i textPos(
		x-metrics.toVirtualX(virtualX),
		y-metrics.toVirtualY(virtualY));

	//printf("text [%s] x = %d y = %d virtualX = %d virtualY = %d fontMetrics->getHeight() = %f\n",text.c_str(),x,y,virtualX,virtualY,fontMetrics->getHeight());

	return textPos;
}

Vec2i computeCenteredPos(const string &text, Font3D *font, int x, int y) {
	if(font == NULL) {
		throw megaglest_runtime_error("font == NULL (2) text = " + text);
	}
	const Metrics &metrics= Metrics::getInstance();
	FontMetrics *fontMetrics= font->getMetrics();

	if(fontMetrics == NULL) {
		throw megaglest_runtime_error("fontMetrics == NULL (2) text = " + text);
	}

	int virtualX = (fontMetrics->getTextWidth(text) > 0 ? static_cast<int>(fontMetrics->getTextWidth(text) / 2.f) : 5);
	int virtualY = (fontMetrics->getHeight(text) > 0 ? static_cast<int>(fontMetrics->getHeight(text) / 2.f) : 5);

	Vec2i textPos(
		x-metrics.toVirtualX(virtualX),
		y-metrics.toVirtualY(virtualY));

	return textPos;
}

void Renderer::renderTextSurroundingBox(int x, int y, int w, int h,
		int maxEditWidth, int maxEditRenderWidth) {
	//glColor4fv(color.ptr());
	//glBegin(GL_QUADS); // Start drawing a quad primitive

	//printf("A w = %d maxEditWidth = %d maxEditRenderWidth = %d\n",w,maxEditWidth,maxEditRenderWidth);
	if(maxEditWidth >= 0 || maxEditRenderWidth >= 0) {
		//printf("B w = %d maxEditWidth = %d maxEditRenderWidth = %d\n",w,maxEditWidth,maxEditRenderWidth);
		if(maxEditRenderWidth >= 0) {
			w = maxEditRenderWidth;
		}
		else {
			w = maxEditWidth;
		}
	}
	//printf("HI!!!\n");
	glPointSize(20.0f);

	int margin = 4;
	//glBegin(GL_POINTS); // Start drawing a point primitive
	glBegin(GL_LINE_LOOP); // Start drawing a line primitive

	glVertex3f(x, y+h, 0.0f); // The bottom left corner
	glVertex3f(x, y-margin, 0.0f); // The top left corner
	glVertex3f(x+w, y-margin, 0.0f); // The top right corner
	glVertex3f(x+w, y+h, 0.0f); // The bottom right corner
	glEnd();
}

void Renderer::renderTextBoundingBox3D(const string &text, Font3D *font,
		float alpha, int x, int y, int w, int h, bool centeredW, bool centeredH,
		bool editModeEnabled,int maxEditWidth, int maxEditRenderWidth) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT);
	glEnable(GL_BLEND);
	glColor4fv(Vec4f(1.f, 1.f, 1.f, alpha).ptr());

	Vec2f pos= Vec2f(x, y);
	//Vec2i pos= centered? computeCenteredPos(text, font, x, y): Vec2i(x, y);
	if(centeredW == true || centeredH == true) {
		getCentered3DPos(text, font, pos, w, h, centeredW, centeredH);
	}

	if(editModeEnabled) {
		if(maxEditWidth >= 0 || maxEditRenderWidth >= 0) {
			int useWidth = maxEditWidth;
			string temp = "";
			for(int i = 0; i < useWidth; ++i) {
				temp += DEFAULT_CHAR_FOR_WIDTH_CALC;
			}
			float lineWidth = (font->getTextHandler()->Advance(temp.c_str()) * ::Shared::Graphics::Font::scaleFontValue);
			useWidth = (int)lineWidth;

			maxEditWidth = useWidth;
		}

		renderTextSurroundingBox(pos.x, pos.y, w, h,maxEditWidth,maxEditRenderWidth);
	}
	glColor4fv(Vec4f(1.f, 1.f, 1.f, alpha).ptr());
	TextRendererSafeWrapper safeTextRender(textRenderer3D,font);
	textRenderer3D->render(text, pos.x, pos.y);
	safeTextRender.end();

	glDisable(GL_BLEND);
	glPopAttrib();
}

void Renderer::renderText3D(const string &text, Font3D *font, float alpha, int x, int y, bool centered) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT);
	glEnable(GL_BLEND);
	glColor4fv(Vec4f(1.f, 1.f, 1.f, alpha).ptr());

	Vec2i pos= Vec2i(x, y);
	//Vec2i pos= centered? computeCenteredPos(text, font, x, y): Vec2i(x, y);

	TextRendererSafeWrapper safeTextRender(textRenderer3D,font);
	textRenderer3D->render(text, pos.x, pos.y, centered);
	//textRenderer3D->end();
	safeTextRender.end();

	glDisable(GL_BLEND);
	glPopAttrib();
}

void Renderer::renderText(const string &text, Font2D *font, float alpha, int x, int y, bool centered) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT);
	glEnable(GL_BLEND);
	glColor4fv(Vec4f(1.f, 1.f, 1.f, alpha).ptr());

	Vec2i pos= centered? computeCenteredPos(text, font, x, y): Vec2i(x, y);

	TextRendererSafeWrapper safeTextRender(textRenderer,font);
	textRenderer->render(text, pos.x, pos.y);
	safeTextRender.end();

	glPopAttrib();
}

Vec2f Renderer::getCentered3DPos(const string &text, Font3D *font, Vec2f &pos, int w, int h,bool centeredW, bool centeredH) {
	if(centeredW == true) {
		if(font == NULL) {
			//abort();
			throw megaglest_runtime_error("font == NULL (5) text = " + text);
		}
		else if(font->getTextHandler() == NULL) {
			char szBuf[8096] = "";
			snprintf(szBuf, 8096, "font->getTextHandler() == NULL(5) text = [%s] FontPtr = [%p]\n", text.c_str(),font);
			throw megaglest_runtime_error(szBuf);
		}

		float lineWidth = (font->getTextHandler()->Advance(text.c_str()) * ::Shared::Graphics::Font::scaleFontValue);
		if(lineWidth < w) {
			pos.x += ((w / 2.f) - (lineWidth / 2.f));
		}
	}

	if(centeredH) {
		if(font == NULL) {
			throw megaglest_runtime_error("font == NULL (6) text = " + text);
		}
		else if(font->getTextHandler() == NULL) {
			throw megaglest_runtime_error("font->getTextHandler() == NULL (6) text = " + text);
		}

		//const Metrics &metrics= Metrics::getInstance();
		//float lineHeight = (font->getTextHandler()->LineHeight(text.c_str()) * Font::scaleFontValue);
		float lineHeight = (font->getTextHandler()->LineHeight(text.c_str()) * ::Shared::Graphics::Font::scaleFontValue);
		//lineHeight=metrics.toVirtualY(lineHeight);
		//lineHeight= lineHeight / (2.f + 0.2f * FontMetrics::DEFAULT_Y_OFFSET_FACTOR);
		//pos.y += (h / 2.f) - (lineHeight / 2.f);
		//pos.y += (h / 2.f) - (lineHeight);
		//pos.y += (lineHeight / 2.f); // y starts at the middle of the render position, so only move up 1/2 the font height

		if(lineHeight < h) {
			//printf("line %d, lineHeight [%f] h [%d] text [%s]\n",__LINE__,lineHeight,h,text.c_str());

			//if(Font::forceFTGLFonts == true) {
				// First go to top of bounding box
				pos.y += (h - lineHeight);
				pos.y -= ((h - lineHeight) / ::Shared::Graphics::Font::scaleFontValueCenterHFactor);
//			}
//			else {
//				pos.y += (float)(((float)h) / 2.0);
//				float heightGap = (float)(((float)h - lineHeight) / 2.0);
//				pos.y -= heightGap;
//
//				//printf("h = %d lineHeight = %f heightGap = %f\n",h,lineHeight,heightGap);
//
//			// Now calculate till we get text to middle
//			//pos.y -= (realHeight / 2);
//			//pos.y += (lineHeight / 2);
//			}
		}
		else if(lineHeight > h) {
			//printf("line %d, lineHeight [%f] h [%d] text [%s]\n",__LINE__,lineHeight,h,text.c_str());

			pos.y += (std::ceil(lineHeight - h));
		}
	}
	return pos;
}

void Renderer::renderTextBoundingBox3D(const string &text, Font3D *font,
		const Vec3f &color, int x, int y, int w, int h, bool centeredW,
		bool centeredH, bool editModeEnabled,int maxEditWidth, int maxEditRenderWidth) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	glPushAttrib(GL_CURRENT_BIT);
	glColor3fv(color.ptr());

	Vec2f pos= Vec2f(x, y);
	//Vec2i pos= centered? computeCenteredPos(text, font, x, y): Vec2i(x, y);

	if(centeredW == true || centeredH == true) {
		getCentered3DPos(text, font, pos, w, h,centeredW,centeredH);
	}

	if(editModeEnabled) {
		if(maxEditWidth >= 0 || maxEditRenderWidth >= 0) {
			int useWidth = maxEditWidth;
			string temp = "";
			for(int i = 0; i < useWidth; ++i) {
				temp += DEFAULT_CHAR_FOR_WIDTH_CALC;
			}
			float lineWidth = (font->getTextHandler()->Advance(temp.c_str()) * ::Shared::Graphics::Font::scaleFontValue);
			useWidth = (int)lineWidth;

			maxEditWidth = useWidth;
		}

		renderTextSurroundingBox(pos.x, pos.y, w, h,maxEditWidth,maxEditRenderWidth);
	}
	glColor3fv(color.ptr());
	TextRendererSafeWrapper safeTextRender(textRenderer3D,font);
	textRenderer3D->render(text, pos.x, pos.y);
	safeTextRender.end();

	glPopAttrib();
}

void Renderer::renderText3D(const string &text, Font3D *font, const Vec3f &color, int x, int y, bool centered) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	glPushAttrib(GL_CURRENT_BIT);
	glColor3fv(color.ptr());

	Vec2i pos= Vec2i(x, y);
	//Vec2i pos= centered? computeCenteredPos(text, font, x, y): Vec2i(x, y);

	TextRendererSafeWrapper safeTextRender(textRenderer3D,font);
	textRenderer3D->render(text, pos.x, pos.y, centered);
	safeTextRender.end();

	glPopAttrib();
}

void Renderer::renderText(const string &text, Font2D *font, const Vec3f &color, int x, int y, bool centered){
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	glPushAttrib(GL_CURRENT_BIT);
	glColor3fv(color.ptr());

	Vec2i pos= centered? computeCenteredPos(text, font, x, y): Vec2i(x, y);

	TextRendererSafeWrapper safeTextRender(textRenderer,font);
	textRenderer->render(text, pos.x, pos.y);
	safeTextRender.end();

	glPopAttrib();
}

void Renderer::renderTextBoundingBox3D(const string &text, Font3D *font,
		const Vec4f &color, int x, int y, int w, int h, bool centeredW,
		bool centeredH, bool editModeEnabled,int maxEditWidth, int maxEditRenderWidth) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT);
	glEnable(GL_BLEND);
	glColor4fv(color.ptr());

	Vec2f pos= Vec2f(x, y);
	//Vec2i pos= centered? computeCenteredPos(text, font, x, y): Vec2i(x, y);

	if(centeredW == true || centeredH == true) {
		getCentered3DPos(text, font, pos, w, h,centeredW,centeredH);
	}

	if(editModeEnabled) {
		if(maxEditWidth >= 0 || maxEditRenderWidth >= 0) {
			int useWidth = maxEditWidth;
			string temp = "";
			for(int i = 0; i < useWidth; ++i) {
				temp += DEFAULT_CHAR_FOR_WIDTH_CALC;
			}
			float lineWidth = (font->getTextHandler()->Advance(temp.c_str()) * ::Shared::Graphics::Font::scaleFontValue);
			useWidth = (int)lineWidth;

			maxEditWidth = useWidth;
		}

		renderTextSurroundingBox(pos.x, pos.y, w, h,maxEditWidth,maxEditRenderWidth);
	}
	glColor4fv(color.ptr());
	TextRendererSafeWrapper safeTextRender(textRenderer3D,font);
	textRenderer3D->render(text, pos.x, pos.y);
	safeTextRender.end();

	glDisable(GL_BLEND);
	glPopAttrib();
}

void Renderer::renderText3D(const string &text, Font3D *font, const Vec4f &color, int x, int y, bool centered) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT);
	glEnable(GL_BLEND);
	glColor4fv(color.ptr());

	Vec2i pos= Vec2i(x, y);
	//Vec2i pos= centered? computeCenteredPos(text, font, x, y): Vec2i(x, y);

	TextRendererSafeWrapper safeTextRender(textRenderer3D,font);
	textRenderer3D->render(text, pos.x, pos.y, centered);
	safeTextRender.end();

	glDisable(GL_BLEND);
	glPopAttrib();
}

void Renderer::renderText(const string &text, Font2D *font, const Vec4f &color, int x, int y, bool centered){
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT);
	glEnable(GL_BLEND);
	glColor4fv(color.ptr());

	Vec2i pos= centered? computeCenteredPos(text, font, x, y): Vec2i(x, y);

	TextRendererSafeWrapper safeTextRender(textRenderer,font);
	textRenderer->render(text, pos.x, pos.y);
	safeTextRender.end();

	glPopAttrib();
}

void Renderer::renderTextShadow3D(const string &text, Font3D *font,const Vec4f &color, int x, int y, bool centered) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	if(font == NULL) {
		throw megaglest_runtime_error("font == NULL (3) text = " + text);
	}

	glPushAttrib(GL_CURRENT_BIT);

	Vec2i pos= centered? computeCenteredPos(text, font, x, y): Vec2i(x, y);

	TextRendererSafeWrapper safeTextRender(textRenderer3D,font);
	if(color.w < 0.5)	{
		glColor3f(0.0f, 0.0f, 0.0f);

		textRenderer3D->render(text, pos.x-1.0f, pos.y-1.0f);
	}
	glColor3f(color.x,color.y,color.z);

	textRenderer3D->render(text, pos.x, pos.y);
	//textRenderer3D->end();
	safeTextRender.end();

	glPopAttrib();
}

void Renderer::renderTextShadow(const string &text, Font2D *font,const Vec4f &color, int x, int y, bool centered){
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	if(font == NULL) {
		throw megaglest_runtime_error("font == NULL (4) text = " + text);
	}

	glPushAttrib(GL_CURRENT_BIT);

	Vec2i pos= centered? computeCenteredPos(text, font, x, y): Vec2i(x, y);

	TextRendererSafeWrapper safeTextRender(textRenderer,font);
	if(color.w < 0.5)	{
		glColor3f(0.0f, 0.0f, 0.0f);

		textRenderer->render(text, pos.x-1.0f, pos.y-1.0f);
	}
	glColor3f(color.x,color.y,color.z);

	textRenderer->render(text, pos.x, pos.y);
	//textRenderer->end();
	safeTextRender.end();

	glPopAttrib();
}

// ============= COMPONENTS =============================

void Renderer::renderLabel(GraphicLabel *label) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	if(label->getEditable() && label->getMaxEditRenderWidth()>0)
	{
	    int x= label->getX();
	    int y= label->getY();
	    int h= label->getH();
	    int w= label->getMaxEditRenderWidth();
	    if(h>0){
	    	//background
	    	glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT);
	    	glEnable(GL_BLEND);

	    	glColor4f(0.2f, 0.2f, 0.2f, 0.6f*label->getFade()) ;

	    	glBegin(GL_TRIANGLE_STRIP);
	    		glVertex2i(x, y);
	    		glVertex2i(x, y+h);
	    		glVertex2i(x+w, y);
	    		glVertex2i(x+w, y+h);
	    	glEnd();
	    	glPopAttrib();
	    }
	}

	if(label->getRenderBackground())
	{
	    int x= label->getX();
	    int y= label->getY();
	    int h= label->getH();
	    int w= label->getW();
	    if(label->getMaxEditRenderWidth()>0){
	    	w= label->getMaxEditRenderWidth();
	    }
	    Vec4f color=label->getBackgroundColor();
	    if(h>0){
	    	//background
	    	glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT);
	    	glEnable(GL_BLEND);

	    	glColor4f(color.x, color.y, color.z, color.w*label->getFade()) ;

	    	glBegin(GL_TRIANGLE_STRIP);
	    		glVertex2i(x, y);
	    		glVertex2i(x, y+h);
	    		glVertex2i(x+w, y);
	    		glVertex2i(x+w, y+h);
	    	glEnd();
	    	glPopAttrib();
	    }
	}

	if(label->getTexture()!=NULL )
	{
	    int x= label->getX();
	    int y= label->getY();
	    int h= label->getH();
	    int w= label->getW();
	    if(h>0){
	    	//background
			glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT);
			glEnable(GL_BLEND);
			glEnable(GL_TEXTURE_2D);
			glBindTexture(GL_TEXTURE_2D, static_cast<Texture2DGl*>( label->getTexture())->getHandle());
	    	glColor4f(1.0f, 1.0f, 1.0f, 1.0f*label->getFade()) ;
	    	glBegin(GL_TRIANGLE_STRIP);
	    		glTexCoord2f(0.f, 0.f);
	    		glVertex2f(x, y);

	    		glTexCoord2f(0.f, 1.f);
	    		glVertex2f(x, y+h);

	    		glTexCoord2f(1.f, 0.f);
	    		glVertex2f(x+w, y);

	    		glTexCoord2f(1.f, 1.f);
	    		glVertex2f(x+w, y+h);
	    	glEnd();
	    	glDisable(GL_TEXTURE_2D);
	    	glPopAttrib();
	    }
	}
	Vec3f labelColor=label->getTextColor();
	Vec4f colorWithAlpha = Vec4f(labelColor.x,labelColor.y,labelColor.z,GraphicComponent::getFade());
	renderLabel(label,&colorWithAlpha);
}

void Renderer::renderLabel(GraphicLabel *label,const Vec3f *color) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	if(color != NULL) {
		Vec4f colorWithAlpha = Vec4f(*color);
		colorWithAlpha.w = GraphicComponent::getFade();
		renderLabel(label,&colorWithAlpha);
	}
	else {
		Vec4f *colorWithAlpha = NULL;
		renderLabel(label,colorWithAlpha);
	}
}

void Renderer::renderLabel(GraphicLabel *label,const Vec4f *color) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	if(label->getVisible() == false) {
		return;
	}
	try {
	glPushAttrib(GL_ENABLE_BIT);
	glEnable(GL_BLEND);

	vector<string> lines;
	string renderTextString = (label->getTextNativeTranslation() != "" ? label->getTextNativeTranslation() : label->getText());
	if(label->getWordWrap() == true) {
		Tokenize(renderTextString,lines,"\n");
	}
	else {
		lines.push_back(renderTextString);
	}

	for(unsigned int i = 0; i < lines.size(); ++i) {
		Vec2i textPos;
		int x= label->getX();
		int y= label->getY() - (i * label->getH());
		int h= label->getH();
		int w= label->getW();
		//if(label->getInstanceName() == "modDescrLabel") printf("~~~ lines.size() [%u] i = %d lines[i] [%s] y = %d\n",lines.size(),i,lines[i].c_str(),y);

		if(label->getCentered()) {
			textPos= Vec2i(x+w/2, y+h/2);
		}
		else {
			textPos= Vec2i(x, y+h/4);
		}

		string renderTextStr = lines[i];
		if(label->getIsPassword() == true) {
			if(renderTextStr != "") {
				renderTextStr = "*****";
			}
		}

		if(color != NULL) {
			if(renderText3DEnabled == true) {
				//renderText3D(lines[i], label->getFont3D(), (*color), textPos.x, textPos.y, label->getCentered());
				//printf("Text Render3D [%s] font3d [%p]\n",lines[i].c_str(),label->getFont3D());
				//printf("Label render C\n");

				renderTextBoundingBox3D(renderTextStr, label->getFont3D(), (*color),
						x, y, w, h, label->getCenteredW(),label->getCenteredH(),
						label->getEditModeEnabled(),label->getMaxEditWidth(),
						label->getMaxEditRenderWidth());
			}
			else {
				//printf("Label render D\n");
				renderText(renderTextStr, label->getFont(), (*color), textPos.x, textPos.y, label->getCentered());
			}
		}
		else {
			if(renderText3DEnabled == true) {
				//renderText3D(lines[i], label->getFont3D(), GraphicComponent::getFade(), textPos.x, textPos.y, label->getCentered());
				//printf("Text Render3D [%s] font3d [%p]\n",lines[i].c_str(),label->getFont3D());
				//printf("Label render E\n");
				renderTextBoundingBox3D(renderTextStr, label->getFont3D(),
						GraphicComponent::getFade(), x, y, w, h,
						label->getCenteredW(),label->getCenteredH(),
						label->getEditModeEnabled(),label->getMaxEditWidth(),
						label->getMaxEditRenderWidth());
			}
			else {
				//printf("Label render F\n");
				renderText(renderTextStr, label->getFont(), GraphicComponent::getFade(), textPos.x, textPos.y, label->getCentered());
			}
		}
	}
	glPopAttrib();
	}
	catch(const exception &e) {
		char szBuf[8096]="";
		snprintf(szBuf,8096,"In [%s::%s Line: %d]\nError [%s] For Control [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,e.what(),label->getInstanceName().c_str());
		SystemFlags::OutputDebug(SystemFlags::debugError,szBuf);
		if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,szBuf);

		throw megaglest_runtime_error(szBuf);
	}

}

void Renderer::renderButton(GraphicButton *button, const Vec4f *fontColorOverride, bool *lightedOverride) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	if(button->getVisible() == false) {
		return;
	}

	try {

	//char szBuf[8096]="";
	//snprintf(szBuf,8096,"In [%s::%s Line: %d]\n For Control container [%s] name [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,button->getContainerName().c_str(), button->getInstanceName().c_str());
	//printf(szBuf);

    int x= button->getX();
    int y= button->getY();
    int h= button->getH();
    int w= button->getW();

	const Vec3f disabledTextColor= Vec3f(0.25f,0.25f,0.25f);

	glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT);

	//background
	CoreData &coreData= CoreData::getInstance();
	Texture2D *backTexture = NULL;

	if(button->getUseCustomTexture() == true) {
		backTexture = dynamic_cast<Texture2D *>(button->getCustomTexture());
	}
	else {
		backTexture = w > 3 * h / 2 ? coreData.getButtonBigTexture(): coreData.getButtonSmallTexture();
	}

	glEnable(GL_TEXTURE_2D);
	glEnable(GL_BLEND);

	if(backTexture != NULL) {
		glBindTexture(GL_TEXTURE_2D, static_cast<Texture2DGl*>(backTexture)->getHandle());
	}
	else {
		glBindTexture(GL_TEXTURE_2D, 0);
	}

	//button
	Vec4f fontColor(GraphicComponent::getCustomTextColor());

	if(fontColorOverride != NULL) {
		fontColor= *fontColorOverride;
	}
	else {
		// white shadowed is default ( in the menu for example )
		fontColor.w = GraphicComponent::getFade();
	}

	//Vec4f color= Vec4f(1.f, 1.f, 1.f, GraphicComponent::getFade());
	Vec4f color= fontColor;
	glColor4fv(color.ptr());

	glBegin(GL_TRIANGLE_STRIP);
		glTexCoord2f(0.f, 0.f);
		glVertex2f(x, y);

		glTexCoord2f(0.f, 1.f);
		glVertex2f(x, y+h);

		glTexCoord2f(1.f, 0.f);
		glVertex2f(x+w, y);

		glTexCoord2f(1.f, 1.f);
		glVertex2f(x+w, y+h);

	glEnd();

	glDisable(GL_TEXTURE_2D);

	//lighting
	float anim= GraphicComponent::getAnim();
	if(anim>0.5f) anim= 1.f-anim;

	bool renderLighted = (button->getLighted() && button->getEditable());
	if(lightedOverride != NULL) {
		renderLighted = *lightedOverride;
	}
	if(renderLighted) {
		const int lightSize= 0;
		const Vec4f color1= Vec4f(color.x, color.y, color.z, 0.1f+anim*0.5f);
		const Vec4f color2= Vec4f(color.x, color.y, color.z, 0.3f+anim);

		glBegin(GL_TRIANGLE_FAN);

		glColor4fv(color2.ptr());
		glVertex2f(x+w/2, y+h/2);

		glColor4fv(color1.ptr());
		glVertex2f(x-lightSize, y-lightSize);

		glColor4fv(color1.ptr());
		glVertex2f(x+w+lightSize, y-lightSize);

		glColor4fv(color1.ptr());
		glVertex2f(x+w+lightSize, y+h+lightSize);

		glColor4fv(color1.ptr());
		glVertex2f(x+w+lightSize, y+h+lightSize);

		glColor4fv(color1.ptr());
		glVertex2f(x-lightSize, y+h+lightSize);

		glColor4fv(color1.ptr());
		glVertex2f(x-lightSize, y-lightSize);

		glEnd();
	}

	//Vec2i textPos= Vec2i(x + w / 2, y + h / 2);

	if(button->getEditable()) {
		if(renderText3DEnabled == true) {
			//renderText3D(button->getText(), button->getFont3D(), color,x + (w / 2), y + (h / 2), true);
			renderTextBoundingBox3D(button->getText(), button->getFont3D(),
					color, x, y, w, h, true, true,false,-1,-1);
		}
		else {
			renderText(button->getText(), button->getFont(), color,x + (w / 2), y + (h / 2), true);
		}
	}
	else {
		if(renderText3DEnabled == true) {
			//renderText3D(button->getText(), button->getFont3D(),disabledTextColor,
			//       x + (w / 2), y + (h / 2), true);
			renderTextBoundingBox3D(button->getText(), button->getFont3D(),disabledTextColor,
						       x, y, w, h, true, true,false,-1,-1);
		}
		else {
			renderText(button->getText(), button->getFont(),disabledTextColor,
			       x + (w / 2), y + (h / 2), true);
		}
	}

    glPopAttrib();
	}
	catch(const exception &e) {
		char szBuf[8096]="";
		snprintf(szBuf,8096,"In [%s::%s Line: %d]\nError [%s] For Control container [%s] name [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,e.what(),button->getContainerName().c_str(), button->getInstanceName().c_str());
		SystemFlags::OutputDebug(SystemFlags::debugError,szBuf);
		if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,szBuf);

		throw megaglest_runtime_error(szBuf);
	}
}

void Renderer::renderCheckBox(const GraphicCheckBox *box) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	if(box->getVisible() == false) {
		return;
	}

    int x= box->getX();
    int y= box->getY();
    int h= box->getH();
    int w= box->getW();

	//const Vec3f disabledTextColor= Vec3f(0.25f,0.25f,0.25f);

	glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT);

	//background
	CoreData &coreData= CoreData::getInstance();
	Texture2D *backTexture= box->getValue()? coreData.getCheckedCheckBoxTexture(): coreData.getCheckBoxTexture();

	glEnable(GL_TEXTURE_2D);
	glEnable(GL_BLEND);

	glBindTexture(GL_TEXTURE_2D, static_cast<Texture2DGl*>(backTexture)->getHandle());

	//box
	Vec4f fontColor;
	//if(game!=NULL){
	//	fontColor=game->getGui()->getDisplay()->getColor();
	//	fontColor.w = GraphicComponent::getFade();
	//}
	//else {
		// white shadowed is default ( in the menu for example )
		fontColor=Vec4f(1.f, 1.f, 1.f, GraphicComponent::getFade());
	//}

	//Vec4f color= Vec4f(1.f, 1.f, 1.f, GraphicComponent::getFade());
	Vec4f color= fontColor;
	glColor4fv(color.ptr());

	glBegin(GL_TRIANGLE_STRIP);
		glTexCoord2f(0.f, 0.f);
		glVertex2f(x, y);

		glTexCoord2f(0.f, 1.f);
		glVertex2f(x, y+h);

		glTexCoord2f(1.f, 0.f);
		glVertex2f(x+w, y);

		glTexCoord2f(1.f, 1.f);
		glVertex2f(x+w, y+h);

	glEnd();

	glDisable(GL_TEXTURE_2D);

	//lighting
	float anim= GraphicComponent::getAnim();
	if(anim > 0.5f) {
		anim = 1.f - anim;
	}

	if(box->getLighted() && box->getEditable()) {
		const int lightSize= 0;
		const Vec4f color1= Vec4f(color.x, color.y, color.z, 0.1f+anim*0.5f);
		const Vec4f color2= Vec4f(color.x, color.y, color.z, 0.3f+anim);

		glBegin(GL_TRIANGLE_FAN);

		glColor4fv(color2.ptr());
		glVertex2f(x+w/2, y+h/2);

		glColor4fv(color1.ptr());
		glVertex2f(x-lightSize, y-lightSize);

		glColor4fv(color1.ptr());
		glVertex2f(x+w+lightSize, y-lightSize);

		glColor4fv(color1.ptr());
		glVertex2f(x+w+lightSize, y+h+lightSize);

		glColor4fv(color1.ptr());
		glVertex2f(x+w+lightSize, y+h+lightSize);

		glColor4fv(color1.ptr());
		glVertex2f(x-lightSize, y+h+lightSize);

		glColor4fv(color1.ptr());
		glVertex2f(x-lightSize, y-lightSize);

		glEnd();
	}

    glPopAttrib();
}


void Renderer::renderLine(const GraphicLine *line) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	if(line->getVisible() == false) {
		return;
	}

    int x= line->getX();
    int y= line->getY();
    int h= line->getH();
    int w= line->getW();

	//const Vec3f disabledTextColor= Vec3f(0.25f,0.25f,0.25f);

	glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT);

	//background
	CoreData &coreData= CoreData::getInstance();
	Texture2D *backTexture= line->getHorizontal()? coreData.getHorizontalLineTexture(): coreData.getVerticalLineTexture();

	glEnable(GL_TEXTURE_2D);
	glEnable(GL_BLEND);

	glBindTexture(GL_TEXTURE_2D, static_cast<Texture2DGl*>(backTexture)->getHandle());

	glBegin(GL_TRIANGLE_STRIP);
		glTexCoord2f(0.f, 0.f);
		glVertex2f(x, y);

		glTexCoord2f(0.f, 1.f);
		glVertex2f(x, y+h);

		glTexCoord2f(1.f, 0.f);
		glVertex2f(x+w, y);

		glTexCoord2f(1.f, 1.f);
		glVertex2f(x+w, y+h);

	glEnd();

	glDisable(GL_TEXTURE_2D);
    glPopAttrib();
}

void Renderer::renderScrollBar(const GraphicScrollBar *sb) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	if(sb->getVisible() == false) {
		return;
	}

    int x= sb->getX();
    int y= sb->getY();
    int h= sb->getH();
    int w= sb->getW();

	//const Vec3f disabledTextColor= Vec3f(0.25f,0.25f,0.25f);

	glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT);
	/////////////////////
	//background
	////////////////////
	CoreData &coreData= CoreData::getInstance();
	Texture2D *backTexture= coreData.getHorizontalLineTexture();

	glEnable(GL_TEXTURE_2D);
	glEnable(GL_BLEND);

	glBindTexture(GL_TEXTURE_2D, static_cast<Texture2DGl*>(backTexture)->getHandle());

	glBegin(GL_TRIANGLE_STRIP);
		glTexCoord2f(0.f, 0.f);
		glVertex2f(x, y);

		glTexCoord2f(0.f, 1.f);
		glVertex2f(x, y+h);

		glTexCoord2f(1.f, 0.f);
		glVertex2f(x+w, y);

		glTexCoord2f(1.f, 1.f);
		glVertex2f(x+w, y+h);

	glEnd();

	////////////////////
	// selectBlock
	////////////////////

    x= sb->getX();
    y= sb->getY();
    h= sb->getH();
    w= sb->getW();

    if( sb->getHorizontal()) {
    	x=x+sb->getVisibleCompPosStart();
    	w=sb->getVisibleCompPosEnd()-sb->getVisibleCompPosStart();
    }
    else {
    	y=y+sb->getVisibleCompPosStart();
    	h=sb->getVisibleCompPosEnd()-sb->getVisibleCompPosStart();
    }

	Texture2D *selectTexture= coreData.getButtonBigTexture();
	assert(selectTexture != NULL);

	glBindTexture(GL_TEXTURE_2D, static_cast<Texture2DGl*>(selectTexture)->getHandle());

	//button
	Vec4f fontColor;
	//if(game!=NULL){
	//	fontColor=game->getGui()->getDisplay()->getColor();
	//	fontColor.w = GraphicComponent::getFade();
	//}
	//else {
		// white shadowed is default ( in the menu for example )
		fontColor=Vec4f(1.f, 1.f, 1.f, GraphicComponent::getFade());
	//}

	//Vec4f color= Vec4f(1.f, 1.f, 1.f, GraphicComponent::getFade());
	Vec4f color= fontColor;
	glColor4fv(color.ptr());

	glBegin(GL_TRIANGLE_STRIP);
		glTexCoord2f(0.f, 0.f);
		glVertex2f(x, y);

		glTexCoord2f(0.f, 1.f);
		glVertex2f(x, y+h);

		glTexCoord2f(1.f, 0.f);
		glVertex2f(x+w, y);

		glTexCoord2f(1.f, 1.f);
		glVertex2f(x+w, y+h);

	glEnd();

	glDisable(GL_TEXTURE_2D);

	//lighting
	float anim= GraphicComponent::getAnim();
	if(anim>0.5f) anim= 1.f-anim;

	if(sb->getLighted() && sb->getEditable()){
		const int lightSize= 0;
		const Vec4f color1= Vec4f(color.x, color.y, color.z, 0.1f+anim*0.5f);
		const Vec4f color2= Vec4f(color.x, color.y, color.z, 0.3f+anim);

		glBegin(GL_TRIANGLE_FAN);

		glColor4fv(color2.ptr());
		glVertex2f(x+w/2, y+h/2);

		glColor4fv(color1.ptr());
		glVertex2f(x-lightSize, y-lightSize);

		glColor4fv(color1.ptr());
		glVertex2f(x+w+lightSize, y-lightSize);

		glColor4fv(color1.ptr());
		glVertex2f(x+w+lightSize, y+h+lightSize);

		glColor4fv(color1.ptr());
		glVertex2f(x+w+lightSize, y+h+lightSize);

		glColor4fv(color1.ptr());
		glVertex2f(x-lightSize, y+h+lightSize);

		glColor4fv(color1.ptr());
		glVertex2f(x-lightSize, y-lightSize);

		glEnd();
	}

    glPopAttrib();
}

void Renderer::renderListBox(GraphicListBox *listBox) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	if(listBox->getVisible() == false) {
		return;
	}
	//if(listBox->getLeftControlled()==true)
	{
	    int x= listBox->getX();
	    int y= listBox->getY();
	    int h= listBox->getH();
	    int w= listBox->getW();
	    if(h>0){
	    	//background
	    	glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT);
	    	glEnable(GL_BLEND);

	    	glColor4f(0.0f, 0.0f, 0.0f, 0.6f*listBox->getFade()) ;

	    	glBegin(GL_TRIANGLE_STRIP);
	    		glVertex2i(x, y);
	    		glVertex2i(x, y+h);
	    		glVertex2i(x+w, y);
	    		glVertex2i(x+w, y+h);
	    	glEnd();
	    	glPopAttrib();
	    }
	}
	renderButton(listBox->getButton1());
    renderButton(listBox->getButton2());

	glPushAttrib(GL_ENABLE_BIT);
	glEnable(GL_BLEND);

	GraphicLabel label("ListBox_render_label","label",false);
	//label.setInstanceName("ListBox_render_label");
	if(listBox->getLeftControlled()==true){
		label.init(listBox->getX()+listBox->getButton1()->getW()+listBox->getButton2()->getW()+2, listBox->getY(), listBox->getW(), listBox->getH(), false,listBox->getTextColor());
	}
	else {
		label.init(listBox->getX(), listBox->getY(), listBox->getW(), listBox->getH(), true,listBox->getTextColor());
	}
	label.setText(listBox->getText());
	label.setTextNativeTranslation(listBox->getTextNativeTranslation());
	label.setFont(listBox->getFont());
	label.setFont3D(listBox->getFont3D());
	renderLabel(&label);


	//lighting

		bool renderLighted= (listBox->getLighted());


		if(renderLighted) {
			float anim= GraphicComponent::getAnim();
			if(anim>0.5f) anim= 1.f-anim;

			Vec3f color=listBox->getTextColor();
		    int x= listBox->getX()+listBox->getButton1()->getW();
		    int y= listBox->getY();
		    int h= listBox->getH();
		    int w= listBox->getW()-listBox->getButton1()->getW()-listBox->getButton2()->getW();

			const int lightSize= 0;
			const Vec4f color1= Vec4f(color.x, color.y, color.z, 0.1f+anim*0.5f);
			const Vec4f color2= Vec4f(color.x, color.y, color.z, 0.3f+anim);

			glBegin(GL_TRIANGLE_FAN);

			glColor4fv(color2.ptr());
			glVertex2f(x+w/2, y+h/2);

			glColor4fv(color1.ptr());
			glVertex2f(x-lightSize, y-lightSize);

			glColor4fv(color1.ptr());
			glVertex2f(x+w+lightSize, y-lightSize);

			glColor4fv(color1.ptr());
			glVertex2f(x+w+lightSize, y+h+lightSize);

			glColor4fv(color1.ptr());
			glVertex2f(x+w+lightSize, y+h+lightSize);

			glColor4fv(color1.ptr());
			glVertex2f(x-lightSize, y+h+lightSize);

			glColor4fv(color1.ptr());
			glVertex2f(x-lightSize, y-lightSize);

			glEnd();
		}

	glPopAttrib();
}

void Renderer::renderMessageBox(GraphicMessageBox *messageBox) {
	const int headerHeight=25;
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	try {
		if(messageBox->getVisible() == false) {
			return;
		}

		if((renderText3DEnabled == false && messageBox->getFont() == NULL) ||
		   (renderText3DEnabled == true && messageBox->getFont3D() == NULL)) {
			messageBox->setFont(CoreData::getInstance().getMenuFontNormal());
			messageBox->setFont3D(CoreData::getInstance().getMenuFontNormal3D());
		}

		string wrappedText = messageBox->getText();
		if(messageBox->getAutoWordWrap() == true) {
			if(renderText3DEnabled == false) {
				wrappedText = messageBox->getFont()->getMetrics()->wordWrapText(wrappedText,messageBox->getW() * 0.90);
			}
			else {
				wrappedText = messageBox->getFont3D()->getMetrics()->wordWrapText(wrappedText,messageBox->getW() * 0.90);
			}
		}

		//background
		glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT);
		glEnable(GL_BLEND);

		glColor4f(0.0f, 0.0f, 0.0f, 0.8f) ;
		glBegin(GL_TRIANGLE_STRIP);
			glVertex2i(messageBox->getX(), messageBox->getY()+messageBox->getH()-headerHeight);
			glVertex2i(messageBox->getX(), messageBox->getY());
			glVertex2i(messageBox->getX() + messageBox->getW(), messageBox->getY() + messageBox->getH()-headerHeight);
			glVertex2i(messageBox->getX() + messageBox->getW(), messageBox->getY());
		glEnd();

		glColor4f(0.0f, 0.0f, 0.0f, 0.8f) ;
		glBegin(GL_TRIANGLE_STRIP);
			glVertex2i(messageBox->getX(), messageBox->getY()+messageBox->getH());
			glVertex2i(messageBox->getX(), messageBox->getY()+messageBox->getH()-headerHeight);
			glVertex2i(messageBox->getX() + messageBox->getW(), messageBox->getY() + messageBox->getH());
			glVertex2i(messageBox->getX() + messageBox->getW(), messageBox->getY()+messageBox->getH()-headerHeight);
		glEnd();

		glBegin(GL_LINE_LOOP);
			glColor4f(0.5f, 0.5f, 0.5f, 0.25f) ;
			glVertex2i(messageBox->getX(), messageBox->getY());

			glColor4f(0.0f, 0.0f, 0.0f, 0.25f) ;
			glVertex2i(messageBox->getX()+ messageBox->getW(), messageBox->getY());

			glColor4f(0.5f, 0.5f, 0.5f, 0.25f) ;
			glVertex2i(messageBox->getX()+ messageBox->getW(), messageBox->getY() + messageBox->getH());

			glColor4f(0.25f, 0.25f, 0.25f, 0.25f) ;
			glVertex2i(messageBox->getX(), messageBox->getY() + messageBox->getH());
		glEnd();

		glBegin(GL_LINE_STRIP);
			glColor4f(1.0f, 1.0f, 1.0f, 0.25f) ;
			glVertex2i(messageBox->getX(), messageBox->getY() + messageBox->getH()-headerHeight);

			glColor4f(0.5f, 0.5f, 0.5f, 0.25f) ;
			glVertex2i(messageBox->getX()+ messageBox->getW(), messageBox->getY() + messageBox->getH()-headerHeight);
		glEnd();

		glPopAttrib();


		//buttons
		for(int i=0; i<messageBox->getButtonCount();i++){

			if((renderText3DEnabled == false && messageBox->getButton(i)->getFont() == NULL) ||
			   (renderText3DEnabled == true && messageBox->getButton(i)->getFont3D() == NULL)) {
				messageBox->getButton(i)->setFont(CoreData::getInstance().getMenuFontNormal());
				messageBox->getButton(i)->setFont3D(CoreData::getInstance().getMenuFontNormal3D());
			}

			renderButton(messageBox->getButton(i));
		}

		Vec4f fontColor;
		//if(game!=NULL){
		//	fontColor=game->getGui()->getDisplay()->getColor();
		//}
		//else {
			// white shadowed is default ( in the menu for example )
			fontColor=Vec4f(1.f, 1.f, 1.f, 1.0f);
		//}

		if(renderText3DEnabled == true) {
			//text
			renderTextShadow3D(
					wrappedText, messageBox->getFont3D(), fontColor,
				messageBox->getX()+15, messageBox->getY()+messageBox->getH()-headerHeight*2,
				false );

			renderTextShadow3D(
				messageBox->getHeader(), messageBox->getFont3D(),fontColor,
				messageBox->getX()+15, messageBox->getY()+messageBox->getH()-headerHeight+8,
				false );

		}
		else {
			//text
			renderTextShadow(
					wrappedText, messageBox->getFont(), fontColor,
				messageBox->getX()+15, messageBox->getY()+messageBox->getH()-headerHeight*2,
				false );

			renderTextShadow(
				messageBox->getHeader(), messageBox->getFont(),fontColor,
				messageBox->getX()+15, messageBox->getY()+messageBox->getH()-headerHeight+8,
				false );
		}
	}
	catch(const exception &e) {
		char szBuf[8096]="";
		snprintf(szBuf,8096,"In [%s::%s Line: %d]\nError [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,e.what());
		SystemFlags::OutputDebug(SystemFlags::debugError,szBuf);
		if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,szBuf);

		throw megaglest_runtime_error(szBuf);
	}
}

// ==================== complex rendering ====================

VisibleQuadContainerVBOCache * Renderer::GetSurfaceVBOs(SurfaceData *cellData) {
	std::map<uint32,VisibleQuadContainerVBOCache >::iterator iterFind = mapSurfaceVBOCache.find(cellData->uniqueId);
	if(iterFind == mapSurfaceVBOCache.end()) {
		Vec2f *texCoords		= &cellData->texCoords[0];
		Vec2f *texCoordsSurface	= &cellData->texCoordsSurface[0];
		Vec3f *vertices			= &cellData->vertices[0];
		Vec3f *normals			= &cellData->normals[0];

		VisibleQuadContainerVBOCache vboCache;

		// Generate And Bind The Vertex Buffer
		glGenBuffersARB( 1, (GLuint*)&vboCache.m_nVBOVertices );					// Get A Valid Name
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, vboCache.m_nVBOVertices );			// Bind The Buffer
		// Load The Data
		glBufferDataARB( GL_ARRAY_BUFFER_ARB,  sizeof(Vec3f) * cellData->bufferCount, vertices, GL_STATIC_DRAW_ARB );
		glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);

		assertGl();
		// Generate And Bind The Texture Coordinate Buffer
		glGenBuffersARB( 1, (GLuint*)&vboCache.m_nVBOFowTexCoords );					// Get A Valid Name
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, vboCache.m_nVBOFowTexCoords );		// Bind The Buffer
		// Load The Data
		glBufferDataARB( GL_ARRAY_BUFFER_ARB, sizeof(Vec2f) * cellData->bufferCount, texCoords, GL_STATIC_DRAW_ARB );
		glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);

		assertGl();
		// Generate And Bind The Texture Coordinate Buffer
		glGenBuffersARB( 1, (GLuint*)&vboCache.m_nVBOSurfaceTexCoords );					// Get A Valid Name
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, vboCache.m_nVBOSurfaceTexCoords );		// Bind The Buffer
		// Load The Data
		glBufferDataARB( GL_ARRAY_BUFFER_ARB, sizeof(Vec2f) * cellData->bufferCount, texCoordsSurface, GL_STATIC_DRAW_ARB );
		glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);

		assertGl();
		// Generate And Bind The Normal Buffer
		glGenBuffersARB( 1, (GLuint*)&vboCache.m_nVBONormals );					// Get A Valid Name
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, vboCache.m_nVBONormals );			// Bind The Buffer
		// Load The Data
		glBufferDataARB( GL_ARRAY_BUFFER_ARB,  sizeof(Vec3f) * cellData->bufferCount, normals, GL_STATIC_DRAW_ARB );
		glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);

		vboCache.hasBuiltVBOs = true;

		mapSurfaceVBOCache[cellData->uniqueId] = vboCache;

		// don't need the data in computer RAM anymore its in the GPU now
		cellData->texCoords.clear();
		cellData->texCoordsSurface.clear();
		cellData->vertices.clear();
		cellData->normals.clear();
	}

	return &mapSurfaceVBOCache[cellData->uniqueId];
}

void Renderer::ReleaseSurfaceVBOs() {
	for(std::map<uint32,VisibleQuadContainerVBOCache>::iterator iterFind = mapSurfaceVBOCache.begin();
			iterFind != mapSurfaceVBOCache.end(); ++iterFind) {

		VisibleQuadContainerVBOCache &item = iterFind->second;
		if(item.hasBuiltVBOs == true) {
			glDeleteBuffersARB( 1, (GLuint*)&item.m_nVBOVertices );					// Get A Valid Name
			glDeleteBuffersARB( 1, (GLuint*)&item.m_nVBOFowTexCoords );					// Get A Valid Name
			glDeleteBuffersARB( 1, (GLuint*)&item.m_nVBOSurfaceTexCoords );					// Get A Valid Name
			glDeleteBuffersARB( 1, (GLuint*)&item.m_nVBONormals );					// Get A Valid Name
			//glDeleteBuffersARB( 1, &item.m_nVBOIndexes );					// Get A Valid Name
		}
	}

	mapSurfaceVBOCache.clear();
}

Renderer::MapRenderer::Layer::~Layer() {
	if(vbo_vertices) glDeleteBuffersARB(1,&vbo_vertices);
	if(vbo_normals) glDeleteBuffersARB(1,&vbo_normals);
	if(vbo_fowTexCoords) glDeleteBuffersARB(1,&vbo_fowTexCoords);
	if(vbo_surfTexCoords) glDeleteBuffersARB(1,&vbo_surfTexCoords);
	if(vbo_indices) glDeleteBuffersARB(1,&vbo_indices);
	
}

template<typename T> void _loadVBO(GLuint &vbo,std::vector<T> buf,int target=GL_ARRAY_BUFFER_ARB) {
	assert(buf.size());
	if(true /* vbo enabled? */) {
		glGenBuffersARB(1,&vbo);
		assert(vbo);
		glBindBufferARB(target,vbo);
		glBufferDataARB(target,sizeof(T)*buf.size(),&buf[0],GL_STATIC_DRAW_ARB);
		glBindBufferARB(target,0);
		assertGl();
		buf.clear();
	}
}

void Renderer::MapRenderer::Layer::load_vbos(bool vboEnabled) {
	indexCount = (int)indices.size();
	if(vboEnabled) {
		_loadVBO(vbo_vertices,vertices);
		_loadVBO(vbo_normals,normals);
		_loadVBO(vbo_fowTexCoords,fowTexCoords);
		_loadVBO(vbo_surfTexCoords,surfTexCoords);

		_loadVBO(vbo_indices,indices,GL_ELEMENT_ARRAY_BUFFER_ARB);
	}
	else {
		vbo_vertices = 0;
		vbo_normals = 0;
		vbo_fowTexCoords = 0;
		vbo_surfTexCoords = 0;
		vbo_indices = 0;
	}
}

void Renderer::MapRenderer::loadVisibleLayers(float coordStep,VisibleQuadContainerCache &qCache) {
	int totalCellCount = 0;
	// we create a layer for each visible texture in the map
	for(int visibleIndex = 0;
			visibleIndex < (int)qCache.visibleScaledCellList.size(); ++visibleIndex) {
		Vec2i &pos = qCache.visibleScaledCellList[visibleIndex];

		totalCellCount++;

		SurfaceCell *tc00= map->getSurfaceCell(pos.x, pos.y);
		SurfaceCell *tc10= map->getSurfaceCell(pos.x+1, pos.y);
		SurfaceCell *tc01= map->getSurfaceCell(pos.x, pos.y+1);
		SurfaceCell *tc11= map->getSurfaceCell(pos.x+1, pos.y+1);

		const Vec2f &surfCoord= tc00->getSurfTexCoord();

		SurfaceCell *tc[4] = {
				tc00,
				tc10,
				tc01,
				tc11
		};
		int textureHandle = static_cast<const Texture2DGl*>(tc[0]->getSurfaceTexture())->getHandle();
		string texturePath = static_cast<const Texture2DGl*>(tc[0]->getSurfaceTexture())->getPath();
		//int32 textureCRC = CalculatePixelsCRC(static_cast<const Texture2DGl*>(tc[0]->getSurfaceTexture()));
		Layer* layer = NULL;
		for(Layers::iterator it= layers.begin(); it!= layers.end(); ++it) {
			if((*it)->textureHandle == textureHandle) {
			//if((*it)->texturePath == texturePath) {
			//if((*it)->textureCRC == textureCRC) {
				layer = *it;
				break;
			}
		}
		if(!layer) {
			layer = new Layer(textureHandle);
			layer->texturePath = texturePath;
			//layer->textureCRC = textureCRC;
			layers.push_back(layer);

			//printf("Ading new unique texture [%s]\n",texturePath.c_str());
		}
		// we'll be super-lazy and re-emit all four corners just because its easier
		int index[4];
		int loopIndexes[4] = { 2,0,3,1 };
		for(int i=0; i < 4; i++) {
			index[i] = (int)layer->vertices.size();
			SurfaceCell *corner = tc[loopIndexes[i]];

			layer->vertices.push_back(corner->getVertex());
			layer->normals.push_back(corner->getNormal());
			layer->fowTexCoords.push_back(corner->getFowTexCoord());
		}

		layer->surfTexCoords.push_back(Vec2f(surfCoord.x, surfCoord.y + coordStep));
		layer->surfTexCoords.push_back(Vec2f(surfCoord.x, surfCoord.y));
		layer->surfTexCoords.push_back(Vec2f(surfCoord.x+coordStep, surfCoord.y+coordStep));
		layer->surfTexCoords.push_back(Vec2f(surfCoord.x+coordStep, surfCoord.y));

		// and make two triangles (no strip, we may be disjoint)
		layer->indices.push_back(index[0]);
		layer->indices.push_back(index[1]);
		layer->indices.push_back(index[2]);
		layer->indices.push_back(index[1]);
		layer->indices.push_back(index[3]);
		layer->indices.push_back(index[2]);

	}
	// turn them into vbos (actually this method will just calc the index count)
	for(Layers::iterator layer= layers.begin(); layer!= layers.end(); ++layer){
		(*layer)->load_vbos(false);
	}

	//printf("Total # of layers for this map = %d totalCellCount = %d overall render reduction ratio = %d times\n",layers.size(),totalCellCount,(totalCellCount / layers.size()));
}

void Renderer::MapRenderer::load(float coordStep) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	int totalCellCount = 0;
	// we create a layer for each texture in the map
	for(int y=0; y<map->getSurfaceH()-1; y++) {
		for(int x=0; x<map->getSurfaceW()-1; x++) {
			totalCellCount++;

			SurfaceCell *tc[4] = {
				map->getSurfaceCell(x,y),
				map->getSurfaceCell(x+1,y),
				map->getSurfaceCell(x,y+1),
				map->getSurfaceCell(x+1,y+1)
			};
			int textureHandle = static_cast<const Texture2DGl*>(tc[0]->getSurfaceTexture())->getHandle();
			string texturePath = static_cast<const Texture2DGl*>(tc[0]->getSurfaceTexture())->getPath();
			//int32 textureCRC = CalculatePixelsCRC(static_cast<const Texture2DGl*>(tc[0]->getSurfaceTexture()));
			Layer* layer = NULL;
			for(Layers::iterator it= layers.begin(); it!= layers.end(); ++it) {
				if((*it)->textureHandle == textureHandle) {
				//if((*it)->texturePath == texturePath) {
				//if((*it)->textureCRC == textureCRC) {
					layer = *it;
					break;
				}
			}
			if(!layer) {
				layer = new Layer(textureHandle);
				layer->texturePath = texturePath;
				//layer->textureCRC = textureCRC;
				layers.push_back(layer);

				//printf("Ading new unique texture [%s]\n",texturePath.c_str());
			}
			// we'll be super-lazy and re-emit all four corners just because its easier
			int index[4];
			int loopIndexes[4] = { 2,0,3,1 };
			for(int i=0; i < 4; i++) {
				index[i] = (int)layer->vertices.size();
				SurfaceCell *corner = tc[loopIndexes[i]];
				layer->vertices.push_back(corner->getVertex());
				layer->normals.push_back(corner->getNormal());
			}

			// the texture coords are all on the current texture obviously
			layer->fowTexCoords.push_back(tc[loopIndexes[0]]->getFowTexCoord());
			layer->fowTexCoords.push_back(tc[loopIndexes[1]]->getFowTexCoord());
			layer->fowTexCoords.push_back(tc[loopIndexes[2]]->getFowTexCoord());
			layer->fowTexCoords.push_back(tc[loopIndexes[3]]->getFowTexCoord());

			layer->surfTexCoords.push_back(tc[0]->getSurfTexCoord()+Vec2f(0,coordStep));
			layer->surfTexCoords.push_back(tc[0]->getSurfTexCoord()+Vec2f(0,0));
			layer->surfTexCoords.push_back(tc[0]->getSurfTexCoord()+Vec2f(coordStep,coordStep));
			layer->surfTexCoords.push_back(tc[0]->getSurfTexCoord()+Vec2f(coordStep,0));

			layer->cellToIndicesMap[Vec2i(x,y)] = (int)layer->indices.size();

			// and make two triangles (no strip, we may be disjoint)
			layer->indices.push_back(index[0]);
			layer->indices.push_back(index[1]);
			layer->indices.push_back(index[2]);
			layer->indices.push_back(index[1]);
			layer->indices.push_back(index[3]);
			layer->indices.push_back(index[2]);
		}
	}
	// turn them into vbos
	for(Layers::iterator layer= layers.begin(); layer!= layers.end(); ++layer){
		(*layer)->load_vbos(true);
	}

	//printf("Total # of layers for this map = %d totalCellCount = %d overall render reduction ratio = %d times\n",layers.size(),totalCellCount,(totalCellCount / layers.size()));
}

template<typename T> void* _bindVBO(GLuint vbo,std::vector<T> &buf,int target=GL_ARRAY_BUFFER_ARB) {
	void* result = NULL;
	if(vbo) {
		glBindBuffer(target,vbo);
	}
	else {
		result = &buf[0];
	}
	return result;
}

void Renderer::MapRenderer::Layer::renderVisibleLayer() {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	//glBindTexture(GL_TEXTURE_2D, static_cast<const Texture2DGl*>(fowTex)->getHandle());
	glClientActiveTexture(Renderer::fowTexUnit);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glTexCoordPointer(2, GL_FLOAT, 0,&fowTexCoords[0]);

	glBindTexture(GL_TEXTURE_2D, textureHandle);
	glClientActiveTexture(Renderer::baseTexUnit);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glTexCoordPointer(2, GL_FLOAT, 0, &surfTexCoords[0]);

	glVertexPointer(3, GL_FLOAT, 0, &vertices[0]);
	glNormalPointer(GL_FLOAT, 0, &normals[0]);

	//glDrawArrays(GL_TRIANGLE_STRIP, 0, vertices.size());
	//unsigned short faceIndices[4] = {0, 1, 2, 3};
	//glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, &faceIndices[0]);
	glDrawElements(GL_TRIANGLES,indexCount,GL_UNSIGNED_INT,&indices[0]);

	glClientActiveTexture(Renderer::fowTexUnit);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glClientActiveTexture(Renderer::baseTexUnit);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}

void Renderer::MapRenderer::Layer::render(VisibleQuadContainerCache &qCache) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	const bool renderOnlyVisibleQuad = true;

	if(renderOnlyVisibleQuad == true) {
		vector<pair<int,int> > rowsToRender;

		if(rowsToRenderCache.find(qCache.lastVisibleQuad) != rowsToRenderCache.end()) {
			rowsToRender = rowsToRenderCache[qCache.lastVisibleQuad];
		}
		else {
			int startIndex = -1;
			int lastValidIndex = -1;

			for(int visibleIndex = 0;
					visibleIndex < (int)qCache.visibleScaledCellList.size(); ++visibleIndex) {
				Vec2i &pos = qCache.visibleScaledCellList[visibleIndex];

				if(cellToIndicesMap.find(pos) != cellToIndicesMap.end()) {
					//printf("Layer Render, visibleindex = %d pos [%s] cellToIndicesMap[pos] = %d lastValidIndex = %d\n",visibleIndex,pos.getString().c_str(),cellToIndicesMap[pos],lastValidIndex);

					if(startIndex < 0 || cellToIndicesMap[pos] == lastValidIndex + 6) {
						lastValidIndex = cellToIndicesMap[pos];
						if(startIndex < 0) {
							startIndex = lastValidIndex;
						}
					}
					else if(startIndex >= 0) {
						rowsToRender.push_back(make_pair(startIndex,lastValidIndex));

						lastValidIndex = cellToIndicesMap[pos];
						startIndex = lastValidIndex;
					}
				}
			}
			if(startIndex >= 0) {
				rowsToRender.push_back(make_pair(startIndex,lastValidIndex));
			}

			rowsToRenderCache[qCache.lastVisibleQuad] = rowsToRender;
		}

		if(rowsToRender.empty() == false) {
			//printf("Layer has %d rows in visible quad, visible quad has %d cells\n",rowsToRender.size(),qCache.visibleScaledCellList.size());

			glVertexPointer(3,GL_FLOAT,0,_bindVBO(vbo_vertices,vertices));
			glNormalPointer(GL_FLOAT,0,_bindVBO(vbo_normals,normals));

			glClientActiveTexture(Renderer::fowTexUnit);
			glTexCoordPointer(2,GL_FLOAT,0,_bindVBO(vbo_fowTexCoords,fowTexCoords));

			glClientActiveTexture(Renderer::baseTexUnit);
			glBindTexture(GL_TEXTURE_2D,textureHandle);
			glTexCoordPointer(2,GL_FLOAT,0,_bindVBO(vbo_surfTexCoords,surfTexCoords));

			for(unsigned int i = 0; i < rowsToRender.size(); ++i) {
				//glDrawElements(GL_TRIANGLES,indexCount,GL_UNSIGNED_INT,_bindVBO(vbo_indices,indices,GL_ELEMENT_ARRAY_BUFFER_ARB));
				glDrawRangeElements(GL_TRIANGLES,rowsToRender[i].first,rowsToRender[i].second,indexCount,GL_UNSIGNED_INT,_bindVBO(vbo_indices,indices,GL_ELEMENT_ARRAY_BUFFER_ARB));
			}
		}
	}
	else {
		glVertexPointer(3,GL_FLOAT,0,_bindVBO(vbo_vertices,vertices));
		glNormalPointer(GL_FLOAT,0,_bindVBO(vbo_normals,normals));

		glClientActiveTexture(Renderer::fowTexUnit);
		glTexCoordPointer(2,GL_FLOAT,0,_bindVBO(vbo_fowTexCoords,fowTexCoords));

		glClientActiveTexture(Renderer::baseTexUnit);
		glBindTexture(GL_TEXTURE_2D,textureHandle);
		glTexCoordPointer(2,GL_FLOAT,0,_bindVBO(vbo_surfTexCoords,surfTexCoords));

		glDrawElements(GL_TRIANGLES,indexCount,GL_UNSIGNED_INT,_bindVBO(vbo_indices,indices,GL_ELEMENT_ARRAY_BUFFER_ARB));
	}
}

void Renderer::MapRenderer::renderVisibleLayers(const Map* map,float coordStep,VisibleQuadContainerCache &qCache) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	if(map != this->map) {
		//printf("New Map loading\n");
		destroy(); // clear any previous map data
		this->map = map;
		loadVisibleLayers(coordStep,qCache);
	}
	else if(lastVisibleQuad != qCache.lastVisibleQuad) {
		//printf("New Visible Quad loading\n");
		destroy(); // clear any previous map data
		this->map = map;
		loadVisibleLayers(coordStep,qCache);
	}

	lastVisibleQuad = qCache.lastVisibleQuad;
	//printf("About to render %d layers\n",layers.size());

	glClientActiveTexture(fowTexUnit);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glClientActiveTexture(baseTexUnit);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_NORMAL_ARRAY);
	for(Layers::iterator layer= layers.begin(); layer!= layers.end(); ++layer)
		(*layer)->renderVisibleLayer();
	glDisableClientState(GL_VERTEX_ARRAY);
	glBindBuffer(GL_ARRAY_BUFFER_ARB,0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB,0);
	glDisableClientState(GL_NORMAL_ARRAY);
	glClientActiveTexture(fowTexUnit);
	glBindTexture(GL_TEXTURE_2D,0);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glClientActiveTexture(baseTexUnit);
	glBindTexture(GL_TEXTURE_2D,0);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	assertGl();
}

void Renderer::MapRenderer::render(const Map* map,float coordStep,VisibleQuadContainerCache &qCache) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	if(map != this->map) {
		destroy(); // clear any previous map data
		this->map = map;
		load(coordStep);
	}

	//printf("About to render %d layers\n",layers.size());

	glClientActiveTexture(fowTexUnit);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glClientActiveTexture(baseTexUnit);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_NORMAL_ARRAY);
	for(Layers::iterator layer= layers.begin(); layer!= layers.end(); ++layer)
		(*layer)->render(qCache);
	glDisableClientState(GL_VERTEX_ARRAY);
	glBindBuffer(GL_ARRAY_BUFFER_ARB,0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB,0);
	glDisableClientState(GL_NORMAL_ARRAY);
	glClientActiveTexture(fowTexUnit);
	glBindTexture(GL_TEXTURE_2D,0);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glClientActiveTexture(baseTexUnit);
	glBindTexture(GL_TEXTURE_2D,0);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	assertGl();
}

void Renderer::MapRenderer::destroy() {
	while(layers.empty() == false) {
		delete layers.back();
		layers.pop_back();
	}
	map = NULL;
}

void Renderer::renderSurface(const int renderFps) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	IF_DEBUG_EDITION(
		if (getDebugRenderer().willRenderSurface()) {
			getDebugRenderer().renderSurface(visibleQuad / Map::cellScale);
		} else {
	)
	assertGl();

	const World *world= game->getWorld();
	const Map *map= world->getMap();
	float coordStep= world->getTileset()->getSurfaceAtlas()->getCoordStep();

	const Texture2D *fowTex= world->getMinimap()->getFowTexture();
	if(fowTex == NULL) {
		return;
	}

	glPushAttrib(GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_FOG_BIT | GL_TEXTURE_BIT);

	glEnable(GL_BLEND);
	glEnable(GL_COLOR_MATERIAL);
	glDisable(GL_ALPHA_TEST);
	glEnable(GL_CULL_FACE);

	//fog of war tex unit
	glActiveTexture(fowTexUnit);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, static_cast<const Texture2DGl*>(fowTex)->getHandle());

	glTexSubImage2D(
		GL_TEXTURE_2D, 0, 0, 0,
		fowTex->getPixmapConst()->getW(), fowTex->getPixmapConst()->getH(),
		GL_ALPHA, GL_UNSIGNED_BYTE, fowTex->getPixmapConst()->getPixels());

	if(shadowsOffDueToMinRender == false) {
		//shadow texture
		if(shadows == sProjected || shadows == sShadowMapping) {
			glActiveTexture(shadowTexUnit);
			glEnable(GL_TEXTURE_2D);

			glBindTexture(GL_TEXTURE_2D, shadowMapHandle);

			static_cast<ModelRendererGl*>(modelRenderer)->setDuplicateTexCoords(true);
			enableProjectiveTexturing();
		}
	}

	const Rect2i mapBounds(0, 0, map->getSurfaceW()-1, map->getSurfaceH()-1);

	glActiveTexture(baseTexUnit);

	VisibleQuadContainerCache &qCache = getQuadCache();
	
	bool useVBORendering = getVBOSupported();
	if(useVBORendering == true) {
		VisibleQuadContainerCache &qCache = getQuadCache();
		//mapRenderer.render(map,coordStep,qCache);
		mapRenderer.renderVisibleLayers(map,coordStep,qCache);
	}
	else if(qCache.visibleScaledCellList.empty() == false) {

		int lastTex=-1;
		//int currTex=-1;

		//Quad2i snapshotOfvisibleQuad = visibleQuad;

		//bool useVertexArrayRendering = getVBOSupported();
		//bool useVertexArrayRendering = false;
		//if(useVertexArrayRendering == false) {
		    //printf("\LEGACY qCache.visibleScaledCellList.size() = %d \n",qCache.visibleScaledCellList.size());

			Vec2f texCoords[4];
			Vec2f texCoordsSurface[4];
			Vec3f vertices[4];
			Vec3f normals[4];
		    glEnableClientState(GL_VERTEX_ARRAY);
		    glEnableClientState(GL_NORMAL_ARRAY);

		    std::map<int,int> uniqueVisibleTextures;
			for(int visibleIndex = 0;
					visibleIndex < (int)qCache.visibleScaledCellList.size(); ++visibleIndex) {
				Vec2i &pos = qCache.visibleScaledCellList[visibleIndex];
				SurfaceCell *tc00= map->getSurfaceCell(pos.x, pos.y);
				int cellTex= static_cast<const Texture2DGl*>(tc00->getSurfaceTexture())->getHandle();

				uniqueVisibleTextures[cellTex]++;
			}

			//printf("Current renders = %d possible = %d\n",qCache.visibleScaledCellList.size(),uniqueVisibleTextures.size());

			for(int visibleIndex = 0;
					visibleIndex < (int)qCache.visibleScaledCellList.size(); ++visibleIndex) {
				Vec2i &pos = qCache.visibleScaledCellList[visibleIndex];

				SurfaceCell *tc00= map->getSurfaceCell(pos.x, pos.y);
				SurfaceCell *tc10= map->getSurfaceCell(pos.x+1, pos.y);
				SurfaceCell *tc01= map->getSurfaceCell(pos.x, pos.y+1);
				SurfaceCell *tc11= map->getSurfaceCell(pos.x+1, pos.y+1);

				if(tc00 == NULL) {
					throw megaglest_runtime_error("tc00 == NULL");
				}
				if(tc10 == NULL) {
					throw megaglest_runtime_error("tc10 == NULL");
				}
				if(tc01 == NULL) {
					throw megaglest_runtime_error("tc01 == NULL");
				}
				if(tc11 == NULL) {
					throw megaglest_runtime_error("tc11 == NULL");
				}

				triangleCount+= 2;
				pointCount+= 4;

				//set texture
				if(tc00->getSurfaceTexture() == NULL) {
					throw megaglest_runtime_error("tc00->getSurfaceTexture() == NULL");
				}
				int currTex= static_cast<const Texture2DGl*>(tc00->getSurfaceTexture())->getHandle();
				if(currTex != lastTex) {
					lastTex = currTex;
					//glBindTexture(GL_TEXTURE_2D, lastTex);
				}

				const Vec2f &surfCoord= tc00->getSurfTexCoord();

				texCoords[0]		= tc01->getFowTexCoord();
				texCoordsSurface[0]	= Vec2f(surfCoord.x, surfCoord.y + coordStep);
				vertices[0]			= tc01->getVertex();
				normals[0]			= tc01->getNormal();;

				texCoords[1]		= tc00->getFowTexCoord();
				texCoordsSurface[1]	= Vec2f(surfCoord.x, surfCoord.y);
				vertices[1]			= tc00->getVertex();
				normals[1]			= tc00->getNormal();

				texCoords[2]		= tc11->getFowTexCoord();
				texCoordsSurface[2]	= Vec2f(surfCoord.x+coordStep, surfCoord.y+coordStep);
				vertices[2]			= tc11->getVertex();
				normals[2]			= tc11->getNormal();

				texCoords[3]		= tc10->getFowTexCoord();
				texCoordsSurface[3]	= Vec2f(surfCoord.x+coordStep, surfCoord.y);
				vertices[3]			= tc10->getVertex();
				normals[3]			= tc10->getNormal();

				//glBindTexture(GL_TEXTURE_2D, static_cast<const Texture2DGl*>(fowTex)->getHandle());
				glClientActiveTexture(fowTexUnit);
				glEnableClientState(GL_TEXTURE_COORD_ARRAY);
				glTexCoordPointer(2, GL_FLOAT, 0,&texCoords[0]);

				glBindTexture(GL_TEXTURE_2D, lastTex);
				glClientActiveTexture(baseTexUnit);
				glEnableClientState(GL_TEXTURE_COORD_ARRAY);
				glTexCoordPointer(2, GL_FLOAT, 0, &texCoordsSurface[0]);

				glVertexPointer(3, GL_FLOAT, 0, &vertices[0]);
				glNormalPointer(GL_FLOAT, 0, &normals[0]);

				glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
				//unsigned short faceIndices[4] = {0, 1, 2, 3};
				//glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, &faceIndices[0]);

				glClientActiveTexture(fowTexUnit);
				glDisableClientState(GL_TEXTURE_COORD_ARRAY);
				glClientActiveTexture(baseTexUnit);
				glDisableClientState(GL_TEXTURE_COORD_ARRAY);

/*
				glBegin(GL_TRIANGLE_STRIP);

				//draw quad using immediate mode
				glMultiTexCoord2fv(fowTexUnit, tc01->getFowTexCoord().ptr());
				glMultiTexCoord2f(baseTexUnit, surfCoord.x, surfCoord.y + coordStep);
				glNormal3fv(tc01->getNormal().ptr());
				glVertex3fv(tc01->getVertex().ptr());

				glMultiTexCoord2fv(fowTexUnit, tc00->getFowTexCoord().ptr());
				glMultiTexCoord2f(baseTexUnit, surfCoord.x, surfCoord.y);
				glNormal3fv(tc00->getNormal().ptr());
				glVertex3fv(tc00->getVertex().ptr());

				glMultiTexCoord2fv(fowTexUnit, tc11->getFowTexCoord().ptr());
				glMultiTexCoord2f(baseTexUnit, surfCoord.x+coordStep, surfCoord.y+coordStep);
				glNormal3fv(tc11->getNormal().ptr());
				glVertex3fv(tc11->getVertex().ptr());

				glMultiTexCoord2fv(fowTexUnit, tc10->getFowTexCoord().ptr());
				glMultiTexCoord2f(baseTexUnit, surfCoord.x + coordStep, surfCoord.y);
				glNormal3fv(tc10->getNormal().ptr());
				glVertex3fv(tc10->getVertex().ptr());

				glEnd();
*/
			}

			glDisableClientState(GL_NORMAL_ARRAY);
			glDisableClientState(GL_VERTEX_ARRAY);

//		}
//		else {
//		    const bool useVBOs = false;
//		    const bool useSurfaceCache = false;
//
//		    std::vector<SurfaceData> surfaceData;
//		    bool recalcSurface = false;
//
//		    if(useSurfaceCache == true) {
//				std::map<string,std::pair<Chrono, std::vector<SurfaceData> > >::iterator iterFind = mapSurfaceData.find(snapshotOfvisibleQuad.getString());
//				if(iterFind == mapSurfaceData.end()) {
//					recalcSurface = true;
//					//printf("#1 Calculating surface for Rendering using VA's [%s]\n",snapshotOfvisibleQuad.getString().c_str());
//				}
///*
//				else if(iterFind->second.first.getMillis() >= 250) {
//					recalcSurface = true;
//					mapSurfaceData.erase(snapshotOfvisibleQuad.getString());
//					//printf("#2 RE-Calculating surface for Rendering using VA's [%s]\n",snapshotOfvisibleQuad.getString().c_str());
//				}
//*/
//		    }
//		    else {
//		    	recalcSurface = true;
//		    }
//
//		    if(recalcSurface == true) {
//				//printf("Calculating surface for Rendering using VA's [%s]\n",snapshotOfvisibleQuad.getString().c_str());
//
//		    	std::vector<SurfaceData> *surface = &surfaceData;
//		    	if(useSurfaceCache == true) {
//					std::pair<Chrono, std::vector<SurfaceData> > &surfaceCacheEntity = mapSurfaceData[snapshotOfvisibleQuad.getString()];
//					surface = &surfaceCacheEntity.second;
//					//surface.reserve(qCache.visibleScaledCellList.size());
//		    	}
//		    	surface->reserve(qCache.visibleScaledCellList.size());
//
//		    	int lastSurfaceDataIndex = -1;
//				for(int visibleIndex = 0;
//						visibleIndex < (int)qCache.visibleScaledCellList.size(); ++visibleIndex) {
//					Vec2i &pos = qCache.visibleScaledCellList[visibleIndex];
//
//					SurfaceCell *tc00= map->getSurfaceCell(pos.x, pos.y);
//					SurfaceCell *tc10= map->getSurfaceCell(pos.x+1, pos.y);
//					SurfaceCell *tc01= map->getSurfaceCell(pos.x, pos.y+1);
//					SurfaceCell *tc11= map->getSurfaceCell(pos.x+1, pos.y+1);
//
//					if(tc00 == NULL) {
//						throw megaglest_runtime_error("tc00 == NULL");
//					}
//					if(tc10 == NULL) {
//						throw megaglest_runtime_error("tc10 == NULL");
//					}
//					if(tc01 == NULL) {
//						throw megaglest_runtime_error("tc01 == NULL");
//					}
//					if(tc11 == NULL) {
//						throw megaglest_runtime_error("tc11 == NULL");
//					}
//
//					triangleCount+= 2;
//					pointCount+= 4;
//
//					//set texture
//					if(tc00->getSurfaceTexture() == NULL) {
//						throw megaglest_runtime_error("tc00->getSurfaceTexture() == NULL");
//					}
//
//					int surfaceDataIndex = -1;
//					currTex= static_cast<const Texture2DGl*>(tc00->getSurfaceTexture())->getHandle();
//					if(currTex != lastTex) {
//						lastTex = currTex;
//					}
//					else {
//						surfaceDataIndex = lastSurfaceDataIndex;
//					}
//
//					if(surfaceDataIndex < 0) {
//						SurfaceData newData;
//						newData.uniqueId = SurfaceData::nextUniqueId;
//						SurfaceData::nextUniqueId++;
//						newData.bufferCount=0;
//						newData.textureHandle = currTex;
//						surface->push_back(newData);
//
//						surfaceDataIndex = (int)surface->size() - 1;
//					}
//
//					lastSurfaceDataIndex = surfaceDataIndex;
//
//					SurfaceData *cellData = &(*surface)[surfaceDataIndex];
//
//					const Vec2f &surfCoord= tc00->getSurfTexCoord();
//
//					cellData->texCoords.push_back(tc01->getFowTexCoord());
//					cellData->texCoordsSurface.push_back(Vec2f(surfCoord.x, surfCoord.y + coordStep));
//					cellData->vertices.push_back(tc01->getVertex());
//					cellData->normals.push_back(tc01->getNormal());
//					cellData->bufferCount++;
//
//					cellData->texCoords.push_back(tc00->getFowTexCoord());
//					cellData->texCoordsSurface.push_back(Vec2f(surfCoord.x, surfCoord.y));
//					cellData->vertices.push_back(tc00->getVertex());
//					cellData->normals.push_back(tc00->getNormal());
//					cellData->bufferCount++;
//
//					cellData->texCoords.push_back(tc11->getFowTexCoord());
//					cellData->texCoordsSurface.push_back(Vec2f(surfCoord.x+coordStep, surfCoord.y+coordStep));
//					cellData->vertices.push_back(tc11->getVertex());
//					cellData->normals.push_back(tc11->getNormal());
//					cellData->bufferCount++;
//
//					cellData->texCoords.push_back(tc10->getFowTexCoord());
//					cellData->texCoordsSurface.push_back(Vec2f(surfCoord.x+coordStep, surfCoord.y));
//					cellData->vertices.push_back(tc10->getVertex());
//					cellData->normals.push_back(tc10->getNormal());
//					cellData->bufferCount++;
//				}
//			}
//
//		    std::vector<SurfaceData> *surface = &surfaceData;
//		    if(useSurfaceCache == true) {
//		    	std::pair<Chrono, std::vector<SurfaceData> > &surfaceCacheEntity = mapSurfaceData[snapshotOfvisibleQuad.getString()];
//		    	surface = &surfaceCacheEntity.second;
//		    }
//
//		    glEnableClientState(GL_VERTEX_ARRAY);
//		    glEnableClientState(GL_NORMAL_ARRAY);
//
//			for(int i = 0; i < (int)surface->size(); ++i) {
//				SurfaceData &data = (*surface)[i];
//
//				if(useVBOs == true) {
//					VisibleQuadContainerVBOCache *vboCache = GetSurfaceVBOs(&data);
//
//					//glBindTexture(GL_TEXTURE_2D, static_cast<const Texture2DGl*>(fowTex)->getHandle());
//					glClientActiveTexture(fowTexUnit);
//					glEnableClientState(GL_TEXTURE_COORD_ARRAY);
//
//					glBindBufferARB( GL_ARRAY_BUFFER_ARB, vboCache->m_nVBOFowTexCoords);
//					glTexCoordPointer(2, GL_FLOAT, 0,(char *) NULL);
//
//					glBindTexture(GL_TEXTURE_2D, data.textureHandle);
//					glClientActiveTexture(baseTexUnit);
//					glEnableClientState(GL_TEXTURE_COORD_ARRAY);
//
//					glBindBufferARB( GL_ARRAY_BUFFER_ARB, vboCache->m_nVBOSurfaceTexCoords);
//					glTexCoordPointer(2, GL_FLOAT, 0, (char *) NULL);
//
//					glBindBufferARB( GL_ARRAY_BUFFER_ARB, vboCache->m_nVBOVertices);
//					glVertexPointer(3, GL_FLOAT, 0, (char *) NULL);
//
//					glBindBufferARB( GL_ARRAY_BUFFER_ARB, vboCache->m_nVBONormals);
//					glNormalPointer(GL_FLOAT, 0, (char *) NULL);
//
//					glDrawArrays(GL_TRIANGLE_STRIP, 0, data.bufferCount);
//
//					glBindBufferARB( GL_ARRAY_BUFFER_ARB, 0 );
//
//					glClientActiveTexture(fowTexUnit);
//					glDisableClientState(GL_TEXTURE_COORD_ARRAY);
//					glClientActiveTexture(baseTexUnit);
//					glDisableClientState(GL_TEXTURE_COORD_ARRAY);
//
//				}
//				else {
//					Vec2f *texCoords		= &data.texCoords[0];
//					Vec2f *texCoordsSurface	= &data.texCoordsSurface[0];
//					Vec3f *vertices			= &data.vertices[0];
//					Vec3f *normals			= &data.normals[0];
//
//					//glBindTexture(GL_TEXTURE_2D, static_cast<const Texture2DGl*>(fowTex)->getHandle());
//					glClientActiveTexture(fowTexUnit);
//					glEnableClientState(GL_TEXTURE_COORD_ARRAY);
//					glTexCoordPointer(2, GL_FLOAT, 0,texCoords);
//
//					glBindTexture(GL_TEXTURE_2D, data.textureHandle);
//					glClientActiveTexture(baseTexUnit);
//					glEnableClientState(GL_TEXTURE_COORD_ARRAY);
//					glTexCoordPointer(2, GL_FLOAT, 0, texCoordsSurface);
//
//					glVertexPointer(3, GL_FLOAT, 0, vertices);
//					glNormalPointer(GL_FLOAT, 0, normals);
//
//					glDrawArrays(GL_TRIANGLE_STRIP, 0, data.bufferCount);
//
//					glClientActiveTexture(fowTexUnit);
//					glDisableClientState(GL_TEXTURE_COORD_ARRAY);
//					glClientActiveTexture(baseTexUnit);
//					glDisableClientState(GL_TEXTURE_COORD_ARRAY);
//				}
//			}
//
//			glDisableClientState(GL_NORMAL_ARRAY);
//			glDisableClientState(GL_VERTEX_ARRAY);
//
//			//printf("Surface Render before [%d] after [%d]\n",qCache.visibleScaledCellList.size(),surface.size());
//		}
	}

	//Restore
	static_cast<ModelRendererGl*>(modelRenderer)->setDuplicateTexCoords(false);

	glDisable(GL_TEXTURE_2D);

	glPopAttrib();

	//assert
	GLenum glresult = glGetError();	//remove when first mtex problem solved
	if(glresult) {
		assertGl();
	}
	assertGl();

	IF_DEBUG_EDITION(
		} // end else, if not renderering debug textures instead of regular terrain
		getDebugRenderer().renderEffects(visibleQuad / Map::cellScale);
	)
}

void Renderer::renderObjects(const int renderFps) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	const World *world= game->getWorld();
	//const Map *map= world->getMap();

	Config &config= Config::getInstance();
	int tilesetObjectsToAnimate=config.getInt("AnimatedTilesetObjects","-1");

    assertGl();

	const Texture2D *fowTex = world->getMinimap()->getFowTexture();
	const Pixmap2D *fowTexPixmap = fowTex->getPixmapConst();
	Vec3f baseFogColor = world->getTileset()->getFogColor() * world->getTimeFlow()->computeLightColor();

    bool modelRenderStarted = false;

	VisibleQuadContainerCache &qCache = getQuadCache();

	//	for(int visibleIndex = 0;
	//			visibleIndex < qCache.visibleObjectList.size(); ++visibleIndex) {
	// render from last to first object so animated objects which are on bottom of screen are
	// rendered first which looks better for limited number of animated tileset objects
	for(int visibleIndex = (int)qCache.visibleObjectList.size()-1;
			visibleIndex >= 0 ; --visibleIndex) {
		Object *o = qCache.visibleObjectList[visibleIndex];

		Model *objModel= o->getModelPtr();
		//objModel->updateInterpolationData(o->getAnimProgress(), true);
		const Vec3f v= o->getConstPos();

		if(modelRenderStarted == false) {
			modelRenderStarted = true;

			glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_FOG_BIT | GL_LIGHTING_BIT | GL_TEXTURE_BIT);

			if(shadowsOffDueToMinRender == false &&
				shadows == sShadowMapping) {
				glActiveTexture(shadowTexUnit);
				glEnable(GL_TEXTURE_2D);

				glBindTexture(GL_TEXTURE_2D, shadowMapHandle);

				static_cast<ModelRendererGl*>(modelRenderer)->setDuplicateTexCoords(true);
				enableProjectiveTexturing();
			}

			glActiveTexture(baseTexUnit);
			glEnable(GL_COLOR_MATERIAL);
			glAlphaFunc(GL_GREATER, 0.5f);

			modelRenderer->begin(true, true, false, false);
		}
		//ambient and diffuse color is taken from cell color

		float fowFactor= fowTexPixmap->getPixelf(o->getMapPos().x / Map::cellScale, o->getMapPos().y / Map::cellScale);
		Vec4f color= Vec4f(Vec3f(fowFactor), 1.f);
		glColor4fv(color.ptr());
		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (color * ambFactor).ptr());
		glFogfv(GL_FOG_COLOR, (baseFogColor * fowFactor).ptr());

		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();
		glTranslatef(v.x, v.y, v.z);
		glRotatef(o->getRotation(), 0.f, 1.f, 0.f);

		//We use OpenGL Lights so no manual action is needed here. In fact this call did bad things on lighting big rocks for example
		//		if(o->getRotation() != 0.0) {
		//			setupLightingForRotatedModel();
		//		}

		//objModel->updateInterpolationData(0.f, true);
		//if(this->gameCamera->getPos().dist(o->getPos()) <= SKIP_INTERPOLATION_DISTANCE) {


		if (tilesetObjectsToAnimate == -1) {
			objModel->updateInterpolationData(o->getAnimProgress(), true);
		} else if (tilesetObjectsToAnimate > 0 && o->isAnimated()) {
			tilesetObjectsToAnimate--;
			objModel->updateInterpolationData(o->getAnimProgress(), true);
		} else {
			objModel->updateInterpolationData(0, true);
		}

//		objModel->updateInterpolationData(o->getAnimProgress(), true);
		//}
		modelRenderer->render(objModel);

		triangleCount+= objModel->getTriangleCount();
		pointCount+= objModel->getVertexCount();

		glPopMatrix();
	}

	if(modelRenderStarted == true) {
		modelRenderer->end();
		glPopAttrib();
	}

	//restore
	static_cast<ModelRendererGl*>(modelRenderer)->setDuplicateTexCoords(true);

	assertGl();
}

void Renderer::renderWater() {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	const World *world= game->getWorld();
	const Map *map= world->getMap();

	const Texture2D *fowTex= world->getMinimap()->getFowTexture();
	if(fowTex == NULL) {
		return;
	}

	float waterAnim= world->getWaterEffects()->getAmin();

	//assert
    assertGl();

	glPushAttrib(GL_TEXTURE_BIT | GL_ENABLE_BIT | GL_CURRENT_BIT);

	//water texture nit
    glDisable(GL_TEXTURE_2D);

	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	if(textures3D) {
		Texture3D *waterTex= world->getTileset()->getWaterTex();
		if(waterTex == NULL) {
			throw megaglest_runtime_error("waterTex == NULL");
		}
		glEnable(GL_TEXTURE_3D);
		glBindTexture(GL_TEXTURE_3D, static_cast<Texture3DGl*>(waterTex)->getHandle());
	}
	else{
		glEnable(GL_COLOR_MATERIAL);
		glColor4f(0.5f, 0.5f, 1.0f, 0.5f);
		glBindTexture(GL_TEXTURE_3D, 0);
	}

	assertGl();

	//fog of War texture Unit
	//const Texture2D *fowTex= world->getMinimap()->getFowTexture();
	glActiveTexture(fowTexUnit);
	glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, static_cast<const Texture2DGl*>(fowTex)->getHandle());
    glActiveTexture(baseTexUnit);

	assertGl();

	int thisTeamIndex= world->getThisTeamIndex();
	bool cellExplored = world->showWorldForPlayer(world->getThisFactionIndex());
	bool closed= false;

	Rect2i boundingRect= visibleQuad.computeBoundingRect();
	Rect2i scaledRect= boundingRect/Map::cellScale;
	scaledRect.clamp(0, 0, map->getSurfaceW()-1, map->getSurfaceH()-1);

	float waterLevel= world->getMap()->getWaterLevel();
    for(int j=scaledRect.p[0].y; j<scaledRect.p[1].y; ++j){
        glBegin(GL_TRIANGLE_STRIP);

		for(int i=scaledRect.p[0].x; i<=scaledRect.p[1].x; ++i){
			SurfaceCell *tc0= map->getSurfaceCell(i, j);
            SurfaceCell *tc1= map->getSurfaceCell(i, j+1);
			if(tc0 == NULL) {
				throw megaglest_runtime_error("tc0 == NULL");
			}
			if(tc1 == NULL) {
				throw megaglest_runtime_error("tc1 == NULL");
			}

            if(cellExplored == false) {
                cellExplored = (tc0->isExplored(thisTeamIndex) || tc1->isExplored(thisTeamIndex));
            }

			if(cellExplored == true && tc0->getNearSubmerged()) {
				glNormal3f(0.f, 1.f, 0.f);
                closed= false;

                triangleCount+= 2;
				pointCount+= 2;

                //vertex 1
				glMaterialfv(
					GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
					computeWaterColor(waterLevel, tc1->getHeight()).ptr());
				glMultiTexCoord2fv(GL_TEXTURE1, tc1->getFowTexCoord().ptr());
                glTexCoord3f(i, 1.f, waterAnim);
				glVertex3f(
					static_cast<float>(i)*Map::mapScale,
					waterLevel,
					static_cast<float>(j+1)*Map::mapScale);

                //vertex 2
				glMaterialfv(
					GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
					computeWaterColor(waterLevel, tc0->getHeight()).ptr());
				glMultiTexCoord2fv(GL_TEXTURE1, tc0->getFowTexCoord().ptr());
                glTexCoord3f(i, 0.f, waterAnim);
                glVertex3f(
					static_cast<float>(i)*Map::mapScale,
					waterLevel,
					static_cast<float>(j)*Map::mapScale);

            }
            else{
				if(closed == false) {
					pointCount+= 2;

					//vertex 1
					glMaterialfv(
						GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
						computeWaterColor(waterLevel, tc1->getHeight()).ptr());
					glMultiTexCoord2fv(GL_TEXTURE1, tc1->getFowTexCoord().ptr());
					glTexCoord3f(i, 1.f, waterAnim);
					glVertex3f(
						static_cast<float>(i)*Map::mapScale,
						waterLevel,
						static_cast<float>(j+1)*Map::mapScale);

					//vertex 2
					glMaterialfv(
						GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
						computeWaterColor(waterLevel, tc0->getHeight()).ptr());
					glMultiTexCoord2fv(GL_TEXTURE1, tc0->getFowTexCoord().ptr());
					glTexCoord3f(i, 0.f, waterAnim);
					glVertex3f(
						static_cast<float>(i)*Map::mapScale,
						waterLevel,
						static_cast<float>(j)*Map::mapScale);

					glEnd();
					glBegin(GL_TRIANGLE_STRIP);
					closed= true;
				}
           	}
        }
        glEnd();
    }

	//restore
	glPopAttrib();

	assertGl();
}

void Renderer::renderTeamColorCircle(){
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	VisibleQuadContainerCache &qCache = getQuadCache();
	if(qCache.visibleQuadUnitList.empty() == false) {

		glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT);
		glDisable(GL_LIGHTING);
		glDisable(GL_TEXTURE_2D);
		glDepthFunc(GL_ALWAYS);
		glDisable(GL_STENCIL_TEST);
		glDisable(GL_CULL_FACE);
		glEnable(GL_BLEND);
		glLineWidth(2.f);

		for(int visibleUnitIndex = 0;
							visibleUnitIndex < (int)qCache.visibleQuadUnitList.size(); ++visibleUnitIndex) {
				Unit *unit = qCache.visibleQuadUnitList[visibleUnitIndex];
				Vec3f currVec= unit->getCurrVectorFlat();
				Vec3f color=unit->getFaction()->getTexture()->getPixmapConst()->getPixel3f(0,0);
				glColor4f(color.x, color.y, color.z, 0.7f);
				renderSelectionCircle(currVec, unit->getType()->getSize(), 0.8f, 0.05f);
			}
		glPopAttrib();
	}
}

void Renderer::renderSpecialHighlightUnits(std::map<int,HighlightSpecialUnitInfo> unitHighlightList) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true || unitHighlightList.empty() == true) {
		return;
	}

	VisibleQuadContainerCache &qCache = getQuadCache();
	if(qCache.visibleQuadUnitList.empty() == false) {

		glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT);
		glDisable(GL_LIGHTING);
		glDisable(GL_TEXTURE_2D);
		glDepthFunc(GL_ALWAYS);
		glDisable(GL_STENCIL_TEST);
		glDisable(GL_CULL_FACE);
		glEnable(GL_BLEND);
		glLineWidth(2.f);

		for(int visibleUnitIndex = 0;
							visibleUnitIndex < (int)qCache.visibleQuadUnitList.size(); ++visibleUnitIndex) {
				Unit *unit = qCache.visibleQuadUnitList[visibleUnitIndex];

				std::map<int,HighlightSpecialUnitInfo>::iterator iterFindSpecialUnit = unitHighlightList.find(unit->getId());
				if(iterFindSpecialUnit != unitHighlightList.end()) {
					Vec3f color=unit->getFaction()->getTexture()->getPixmapConst()->getPixel3f(0,0);
					float radius = 1.0f;
					float thickness = 0.1f;
					float alpha = 0.65f;

					HighlightSpecialUnitInfo &specialInfo = iterFindSpecialUnit->second;
					if(specialInfo.color.x >= 0) {
						color.x = specialInfo.color.x;
						color.y = specialInfo.color.y;
						color.z = specialInfo.color.z;
					}
					if(specialInfo.color.w >= 0) {
						alpha = specialInfo.color.w;
					}
					if(specialInfo.radius > 0) {
						radius = specialInfo.radius;
					}
					if(specialInfo.thickness > 0) {
						thickness = specialInfo.thickness;
					}

					glColor4f(color.x, color.y, color.z, alpha);

					Vec3f currVec= unit->getCurrVectorFlat();
					renderSelectionCircle(currVec, unit->getType()->getSize(), radius, thickness);
				}
		}
		glPopAttrib();
	}
}

void Renderer::renderTeamColorPlane(){
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	VisibleQuadContainerCache &qCache = getQuadCache();
	if(qCache.visibleQuadUnitList.empty() == false){
		glPushAttrib(GL_ENABLE_BIT);
		glDisable(GL_LIGHTING);
		glEnable(GL_TEXTURE_2D);
		glEnable(GL_BLEND);
		glEnable(GL_COLOR_MATERIAL);
		const Texture2D *texture=CoreData::getInstance().getTeamColorTexture();
		for(int visibleUnitIndex = 0;
				visibleUnitIndex < (int)qCache.visibleQuadUnitList.size(); ++visibleUnitIndex){
			Unit *unit = qCache.visibleQuadUnitList[visibleUnitIndex];
			if( unit->isAlive()){
				Vec3f currVec= unit->getCurrVectorFlat();
				renderTeamColorEffect(currVec,visibleUnitIndex,unit->getType()->getSize(),
						unit->getFaction()->getTexture()->getPixmapConst()->getPixel3f(0,0),texture);
			}
		}
		glDisable(GL_COLOR_MATERIAL);
		glPopAttrib();
	}
}

void Renderer::renderGhostModel(const UnitType *building, const Vec2i pos,CardinalDir facing, Vec4f *forceColor) {
	//const UnitType *building= gui->getBuilding();
	//const Vec2i &pos= gui->getPosObjWorld();

	//const Gui *gui= game->getGui();
	//const Mouse3d *mouse3d= gui->getMouse3d();
	const Map *map= game->getWorld()->getMap();
	if(map == NULL) {
		char szBuf[8096]="";
		snprintf(szBuf,8096,"In [%s::%s] Line: %d map == NULL",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
		throw megaglest_runtime_error(szBuf);
	}

	glPushMatrix();
	Vec3f pos3f= Vec3f(pos.x, map->getCell(pos)->getHeight(), pos.y);

	//selection building placement
	float offset= building->getSize()/2.f-0.5f;
	glTranslatef(pos3f.x+offset, pos3f.y, pos3f.z+offset);

	//choose color
	Vec4f color;
	if(forceColor != NULL) {
		color = *forceColor;
	}
	else {
		if(map->isFreeCells(pos, building->getSize(), fLand)) {
			color= Vec4f(1.f, 1.f, 1.f, 0.5f);
		}
		else {
//			Uint64 tc=game->getTickCount();
//			float red=0.49f+((tc%4*1.0f)/2);
			color= Vec4f(1.0f, 0.f, 0.f, 0.5f);
		}
	}

	glColor4fv(color.ptr());
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color.ptr());
	Model *buildingModel= building->getFirstStOfClass(scStop)->getAnimation();

	if(facing != CardinalDir::NORTH) {
		float rotateAmount = facing * 90.f;
		if(rotateAmount > 0) {
			glRotatef(rotateAmount, 0.f, 1.f, 0.f);
		}
	}

	buildingModel->updateInterpolationData(0.f, false);
	modelRenderer->render(buildingModel);

	glPopMatrix();
}

void Renderer::renderUnits(bool airUnits, const int renderFps) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	//Unit *unit=NULL;
	//const World *world= game->getWorld();
	MeshCallbackTeamColor meshCallbackTeamColor;

	//assert
	assertGl();

	if(visibleFrameUnitList.empty() == false) {
		visibleFrameUnitList.clear();
		//visibleFrameUnitListCameraKey = "";
		//if(visibleFrameUnitListCameraKey != game->getGameCamera()->getCameraMovementKey()) {
		//	worldToScreenPosCache.clear();
		//}
	}

	VisibleQuadContainerCache &qCache = getQuadCache();
	if(qCache.visibleQuadUnitList.empty() == false) {
		bool modelRenderStarted = false;
		for(int visibleUnitIndex = 0;
				visibleUnitIndex < (int)qCache.visibleQuadUnitList.size(); ++visibleUnitIndex) {
			Unit *unit = qCache.visibleQuadUnitList[visibleUnitIndex];

			if(( airUnits==false && unit->getType()->getField()==fAir) || ( airUnits==true && unit->getType()->getField()!=fAir)){
				continue;
			}
			meshCallbackTeamColor.setTeamTexture(unit->getFaction()->getTexture());

			if(modelRenderStarted == false) {
				modelRenderStarted = true;

				glPushAttrib(GL_ENABLE_BIT | GL_FOG_BIT | GL_LIGHTING_BIT | GL_TEXTURE_BIT);
				glEnable(GL_COLOR_MATERIAL);

				if(!shadowsOffDueToMinRender) {
					if(shadows == sShadowMapping) {
						glActiveTexture(shadowTexUnit);
						glEnable(GL_TEXTURE_2D);

						glBindTexture(GL_TEXTURE_2D, shadowMapHandle);

						static_cast<ModelRendererGl*>(modelRenderer)->setDuplicateTexCoords(true);
						enableProjectiveTexturing();
					}
				}
				glActiveTexture(baseTexUnit);

				modelRenderer->begin(true, true, true, false, &meshCallbackTeamColor);
			}

			glMatrixMode(GL_MODELVIEW);
			glPushMatrix();

			//translate
			Vec3f currVec= unit->getCurrVectorFlat();
			glTranslatef(currVec.x, currVec.y, currVec.z);

			//rotate
			float zrot=unit->getRotationZ();
			float xrot=unit->getRotationX();
			if(zrot!=.0f){
				glRotatef(zrot, 0.f, 0.f, 1.f);
			}
			if(xrot!=.0f){
				glRotatef(xrot, 1.f, 0.f, 0.f);
			}
			glRotatef(unit->getRotation(), 0.f, 1.f, 0.f);

			//dead alpha
			const SkillType *st= unit->getCurrSkill();
			if(st->getClass() == scDie && static_cast<const DieSkillType*>(st)->getFade()) {
				float alpha= 1.0f - unit->getAnimProgressAsFloat();
				glDisable(GL_COLOR_MATERIAL);
				glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Vec4f(1.0f, 1.0f, 1.0f, alpha).ptr());
			}
			else {
				glEnable(GL_COLOR_MATERIAL);
				// we cut off a tiny bit here to avoid problems with fully transparent texture parts cutting units in background rendered later.
				glAlphaFunc(GL_GREATER, 0.02f);
			}

			//render
			Model *model= unit->getCurrentModelPtr();
			//printf("Rendering model [%d - %s]\n[%s]\nCamera [%s]\nDistance: %f\n",unit->getId(),unit->getType()->getName().c_str(),unit->getCurrVector().getString().c_str(),this->gameCamera->getPos().getString().c_str(),this->gameCamera->getPos().dist(unit->getCurrVector()));

			//if(this->gameCamera->getPos().dist(unit->getCurrVector()) <= SKIP_INTERPOLATION_DISTANCE) {
				model->updateInterpolationData(unit->getAnimProgressAsFloat(), unit->isAlive() && !unit->isAnimProgressBound());
			//}

			modelRenderer->render(model);
			triangleCount+= model->getTriangleCount();
			pointCount+= model->getVertexCount();

			glPopMatrix();
			unit->setVisible(true);

			if(	showDebugUI == true &&
				(showDebugUILevel & debugui_unit_titles) == debugui_unit_titles) {

				unit->setScreenPos(computeScreenPosition(currVec));
				visibleFrameUnitList.push_back(unit);
				visibleFrameUnitListCameraKey = game->getGameCamera()->getCameraMovementKey();
			}
		}

		if(modelRenderStarted == true) {
			modelRenderer->end();
			glPopAttrib();
		}
	}

	//restore
	static_cast<ModelRendererGl*>(modelRenderer)->setDuplicateTexCoords(true);

	// reset alpha
	glAlphaFunc(GL_GREATER, 0.0f);
	//assert
	assertGl();

}

void Renderer::renderUnitsToBuild(const int renderFps) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	//assert
	assertGl();

	VisibleQuadContainerCache &qCache = getQuadCache();
	if(qCache.visibleQuadUnitBuildList.empty() == false) {

		glMatrixMode(GL_MODELVIEW);
		glPushAttrib(GL_CURRENT_BIT | GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT);
		glEnable(GL_BLEND);
		glDisable(GL_STENCIL_TEST);
		glDepthFunc(GL_LESS);
		glEnable(GL_COLOR_MATERIAL);
		glDepthMask(GL_FALSE);

		modelRenderer->begin(true, true, false, false);

		for(int visibleUnitIndex = 0;
				visibleUnitIndex < (int)qCache.visibleQuadUnitBuildList.size(); ++visibleUnitIndex) {
			const UnitBuildInfo &buildUnit = qCache.visibleQuadUnitBuildList[visibleUnitIndex];
			//Vec4f modelColor= Vec4f(0.f, 1.f, 0.f, 0.5f);
			const Vec3f teamColor = buildUnit.unit->getFaction()->getTexture()->getPixmapConst()->getPixel3f(0,0);
			Vec4f modelColor= Vec4f(teamColor.x,teamColor.y,teamColor.z,0.4f);
			renderGhostModel(buildUnit.buildUnit, buildUnit.pos, buildUnit.facing, &modelColor);

			//printf("Rendering to build unit index = %d\n",visibleUnitIndex);
		}

		modelRenderer->end();

		glDisable(GL_COLOR_MATERIAL);
		glPopAttrib();
	}

	//assert
	assertGl();

}

void Renderer::renderTeamColorEffect(Vec3f &v, int heigth, int size, Vec3f color, const Texture2D *texture) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	//GLUquadricObj *disc;
	float halfSize=size;
	//halfSize=halfSize;
	float heigthoffset=0.5+heigth%25*0.004;
	glPushMatrix();
	glBindTexture(GL_TEXTURE_2D, static_cast<const Texture2DGl*>(texture)->getHandle());
	glColor4f(color.x, color.y, color.z, 1.0f);
		glBegin(GL_TRIANGLE_STRIP);
			glTexCoord2i(0, 1);
			glVertex3f(v.x-halfSize,v.y+heigthoffset,v.z+halfSize);
			glTexCoord2i(0, 0);
			glVertex3f(v.x-halfSize,v.y+heigthoffset, v.z-halfSize);
			glTexCoord2i(1, 1);

			glVertex3f(v.x+halfSize,v.y+heigthoffset, v.z+halfSize);
			glTexCoord2i(1, 0);
			glVertex3f(v.x+halfSize,v.y+heigthoffset, v.z-halfSize);
		glEnd();
	glPopMatrix();

}

void Renderer::renderMorphEffects(){
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	VisibleQuadContainerCache &qCache = getQuadCache();
	if(qCache.visibleQuadUnitList.empty() == false) {
		bool initialized=false;
		int frameCycle=0;
		for(int visibleUnitIndex = 0;
							visibleUnitIndex < (int)qCache.visibleQuadUnitList.size(); ++visibleUnitIndex) {
			Unit *unit = qCache.visibleQuadUnitList[visibleUnitIndex];
			if(unit->getCurrSkill() != NULL && unit->getCurrSkill()->getClass() == scMorph) {
				Command *command= unit->getCurrCommand();
				if(command != NULL && command->getCommandType()->commandTypeClass == ccMorph){
					const MorphCommandType *mct= static_cast<const MorphCommandType*>(command->getCommandType());
					const UnitType* mType=mct->getMorphUnit();

					if(mType->getSize()>unit->getType()->getSize() ||
							mType->getField()!=unit->getType()->getField()){
						if(!initialized){
							const World *world= game->getWorld();
							frameCycle=world->getFrameCount() % 40;

							glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT);
							glDisable(GL_LIGHTING);
							glDisable(GL_TEXTURE_2D);
							glDepthFunc(GL_ALWAYS);
							glDisable(GL_STENCIL_TEST);
							glDisable(GL_CULL_FACE);
							glEnable(GL_BLEND);
							glLineWidth(2.f);
							initialized=true;
						}

						Vec3f currVec= unit->getCurrVectorFlat();
						currVec=Vec3f(currVec.x,currVec.y+0.3f,currVec.z);
						if(mType->getField() == fAir && unit->getType()->getField()== fLand) {
							currVec=Vec3f(currVec.x,currVec.y+game->getWorld()->getTileset()->getAirHeight(),currVec.z);
						}
						if(mType->getField() == fLand && unit->getType()->getField()== fAir) {
							currVec=Vec3f(currVec.x,currVec.y-game->getWorld()->getTileset()->getAirHeight(),currVec.z);
						}

						float color=frameCycle*0.4f/40;
						glColor4f(color,color, 0.4f, 0.4f);
						renderSelectionCircle(currVec, mType->getSize(), frameCycle*0.85f/40, 0.2f);
					}
				}
			}
		}
		if(initialized) {
			glPopAttrib();
		}
	}
}



void Renderer::renderSelectionEffects(int healthbarMode) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	Config &config= Config::getInstance();
	if(config.getBool("RecordMode","false") == true) {
		return;
	}

	const World *world= game->getWorld();
	const Map *map= world->getMap();
	const Selection *selection= game->getGui()->getSelection();
	const Object *selectedResourceObject= game->getGui()->getSelectedResourceObject();

	glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT);
	glDisable(GL_LIGHTING);
	glDisable(GL_TEXTURE_2D);
	glDepthFunc(GL_ALWAYS);
	glDisable(GL_STENCIL_TEST);
	glDisable(GL_CULL_FACE);
	glEnable(GL_BLEND);
	glLineWidth(2.f);

	//units
	for(int i=0; i<selection->getCount(); ++i){

		const Unit *unit= selection->getUnit(i);
		if(unit != NULL) {
			//translate
			Vec3f currVec= unit->getCurrVectorFlat();
			currVec.y+= 0.3f;

			//selection circle
			int finalHealthbarMode = hbvUndefined;
			if(healthbarMode == hbvUndefined) {
				finalHealthbarMode = unit->getFaction()->getType()->getHealthbarVisible();
			}
			else {
				finalHealthbarMode = healthbarMode;
			}
			bool healthbarsVisible =((finalHealthbarMode & hbvAlways)   ||
					                 (finalHealthbarMode & hbvSelected) ||
					                 (finalHealthbarMode & hbvIfNeeded));
			float selectionCircleThickness = 0.2f;
			float hpRatio = unit->getHpRatio();
			if(healthbarsVisible) {
				selectionCircleThickness = 0.05f;
				hpRatio = 1.0f;
			}

			if(world->getThisFactionIndex() == unit->getFactionIndex()) {
				if(	showDebugUI == true &&
					((showDebugUILevel & debugui_unit_titles) == debugui_unit_titles) &&
					unit->getCommandSize() > 0 &&
					dynamic_cast<const BuildCommandType *>(unit->getCurrCommand()->getCommandType()) != NULL) {
					glColor4f(hpRatio, hpRatio, hpRatio, 0.3f);
				}
				else {
					glColor4f(0, hpRatio, 0, 0.3f);
				}
			}
			else if ( world->getThisTeamIndex() == unit->getTeam()) {
				glColor4f(hpRatio, hpRatio, 0, 0.3f);
			}
			else {
				glColor4f(hpRatio, 0, 0, 0.3f);
			}
			renderSelectionCircle(currVec, unit->getType()->getSize(), selectionCircleRadius,selectionCircleThickness);

			if(	showDebugUI == true &&
				(showDebugUILevel & debugui_unit_titles) == debugui_unit_titles) {

				const UnitPathInterface *path= unit->getPath();
				const UnitPathBasic *pathfinder = (path == NULL ? NULL : dynamic_cast<const UnitPathBasic *>(path));
				if(pathfinder != NULL) {
					vector<Vec2i> pathList = pathfinder->getQueue();

					Vec2i lastPosValue;
					for(int i = 0; i < (int)pathList.size(); ++i) {
						Vec2i curPosValue = pathList[i];
						if(i == 0) {
							lastPosValue = curPosValue;
						}
						Vec3f currVec2 = unit->getVectorFlat(lastPosValue,curPosValue);
						currVec2.y+= 0.3f;
						renderSelectionCircle(currVec2, 1, selectionCircleRadius);
					}
				}
			}

			//magic circle
			if(!healthbarsVisible && world->getThisFactionIndex() == unit->getFactionIndex() && unit->getType()->getMaxEp() > 0) {
				glColor4f(unit->getEpRatio()/2.f, unit->getEpRatio(), unit->getEpRatio(), 0.5f);
				renderSelectionCircle(currVec, unit->getType()->getSize(), magicCircleRadius);
			}

			// Render Attack-boost circles
			if(showDebugUI == true) {
				//const std::pair<const SkillType *,std::vector<Unit *> > &currentAttackBoostUnits = unit->getCurrentAttackBoostUnits();
				const UnitAttackBoostEffectOriginator &effect = unit->getAttackBoostOriginatorEffect();

				if(effect.skillType->isAttackBoostEnabled() == true) {
					glColor4f(MAGENTA.x,MAGENTA.y,MAGENTA.z,MAGENTA.w);
					renderSelectionCircle(currVec, 1, effect.skillType->getAttackBoost()->radius, .25f/effect.skillType->getAttackBoost()->radius);

					for(unsigned int i = 0; i < effect.currentAttackBoostUnits.size(); ++i) {
						// Remove attack boost upgrades from unit
						int findUnitId = effect.currentAttackBoostUnits[i];
						Unit *affectedUnit = game->getWorld()->findUnitById(findUnitId);
						if(affectedUnit != NULL) {
							Vec3f currVecBoost = affectedUnit->getCurrVectorFlat();
							currVecBoost.y += 0.3f;

							renderSelectionCircle(currVecBoost, affectedUnit->getType()->getSize(), 1.f);
						}
					}
				}
			}
		}
	}

	if(selectedResourceObject != NULL && selectedResourceObject->getResource() != NULL && selection->getCount() < 1) {
		Resource *r= selectedResourceObject->getResource();
		int defaultValue= r->getType()->getDefResPerPatch();
		float colorValue=static_cast<float>(r->getAmount())/static_cast<float>(defaultValue);
		glColor4f(0.1f, 0.1f , colorValue, 0.4f);
		renderSelectionCircle(selectedResourceObject->getPos(),2, selectionCircleRadius);
	}
	//target arrow
	if(selection->getCount() == 1) {
		const Unit *unit= selection->getUnit(0);
		if(unit != NULL) {
			//comand arrow
			if(focusArrows && unit->anyCommand()) {
				const CommandType *ct= unit->getCurrCommand()->getCommandType();
				if(ct->getClicks() != cOne){

					//arrow color
					Vec3f arrowColor;
					switch(ct->getClass()) {
					case ccMove:
						arrowColor= Vec3f(0.f, 1.f, 0.f);
						break;
					case ccAttack:
					case ccAttackStopped:
						arrowColor= Vec3f(1.f, 0.f, 0.f);
						break;
					default:
						arrowColor= Vec3f(1.f, 1.f, 0.f);
						break;
					}

					//arrow target
					Vec3f arrowTarget;
					Command *c= unit->getCurrCommand();
					if(c->getUnit() != NULL) {
						arrowTarget= c->getUnit()->getCurrVectorFlat();
					}
					else {
						Vec2i pos= c->getPos();
						map->clampPos(pos);

						arrowTarget= Vec3f(pos.x, map->getCell(pos)->getHeight(), pos.y);
					}

					renderArrow(unit->getCurrVectorFlat(), arrowTarget, arrowColor, 0.3f);
				}
			}

			//meeting point arrow
			if(unit->getType()->getMeetingPoint()) {
				Vec2i pos= unit->getMeetingPos();
				map->clampPos(pos);

				Vec3f arrowTarget= Vec3f(pos.x, map->getCell(pos)->getHeight(), pos.y);
				renderArrow(unit->getCurrVectorFlat(), arrowTarget, Vec3f(0.f, 0.f, 1.f), 0.3f);
			}
		}
	}

	//render selection hightlights
	if(game->getGui()->getHighlightedUnit() != NULL)	{
		const Unit *unit=game->getGui()->getHighlightedUnit() ;

		if(unit->isHighlighted()) {
			float highlight= unit->getHightlight();
			if(game->getWorld()->getThisFactionIndex() == unit->getFactionIndex()) {
				glColor4f(0.f, 1.f, 0.f, highlight);
			}
			else{
				glColor4f(1.f, 0.f, 0.f, highlight);
			}

			Vec3f v= unit->getCurrVectorFlat();
			v.y+= 0.3f;
			renderSelectionCircle(v, unit->getType()->getSize(), 0.5f+0.4f*highlight );
		}
	}
// old inefficient way to render highlights
//	for(int i=0; i < world->getFactionCount(); ++i) {
//		for(int j=0; j < world->getFaction(i)->getUnitCount(); ++j) {
//			const Unit *unit= world->getFaction(i)->getUnit(j);
//
//			if(unit->isHighlighted()) {
//				float highlight= unit->getHightlight();
//				if(game->getWorld()->getThisFactionIndex() == unit->getFactionIndex()) {
//					glColor4f(0.f, 1.f, 0.f, highlight);
//				}
//				else{
//					glColor4f(1.f, 0.f, 0.f, highlight);
//				}
//
//				Vec3f v= unit->getCurrVectorFlat();
//				v.y+= 0.3f;
//				renderSelectionCircle(v, unit->getType()->getSize(), 0.5f+0.4f*highlight );
//			}
//		}
//	}
	//render resource selection highlight
	if(game->getGui()->getHighlightedResourceObject() != NULL) {
		const Object* object=game->getGui()->getHighlightedResourceObject();
		if(object->isHighlighted()) {
			float highlight= object->getHightlight();
			glColor4f(0.1f, 0.1f , 1.0f, highlight);
			Vec3f v= object->getPos();
			v.y+= 0.3f;
			renderSelectionCircle(v, 2, 0.4f+0.4f*highlight );
		}
	}

	glPopAttrib();
}

void Renderer::renderHealthBars(int healthbarMode){
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	Config &config= Config::getInstance();
	if(config.getBool("RecordMode","false") == true) {
		return;
	}

	if(config.getBool("PhotoMode")) {
		return;
	}

	glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT);
	glDisable(GL_LIGHTING);
	glDisable(GL_TEXTURE_2D);
	glDepthFunc(GL_ALWAYS);
	glDisable(GL_STENCIL_TEST);
	glDisable(GL_CULL_FACE);
	glEnable(GL_BLEND);
	glLineWidth(2.f);

	VisibleQuadContainerCache &qCache = getQuadCache();
	if(qCache.visibleQuadUnitList.empty() == false) {
		for(int visibleUnitIndex = 0;
				visibleUnitIndex < (int)qCache.visibleQuadUnitList.size(); ++visibleUnitIndex) {
			Unit *unit = qCache.visibleQuadUnitList[visibleUnitIndex];
			if(isHealthBarVisible(unit,healthbarMode)) {
				float healthbarheight;
				float healthbarthickness;
				const Texture2D *healthbarTexture;
				const Texture2D *healthbarBackgroundTexture;
				bool healthbarLineBorder;

				//get settings of the faction
				healthbarheight=unit->getFaction()->getType()->getHealthbarHeight();
				healthbarthickness=unit->getFaction()->getType()->getHealthbarThickness();
				healthbarLineBorder=unit->getFaction()->getType()->isHealthbarLineBorder();
				CoreData &coreData= CoreData::getInstance();
				//First try faction texture then use core Texture
				if(unit->getFaction()->getType()->isHealthbarBorderTextureEnabled()) {
					healthbarTexture=unit->getFaction()->getType()->getHealthbarTexture();
					if(healthbarTexture==NULL) {
						healthbarTexture=coreData.getHealthbarTexture();
					}
				} else {
					healthbarTexture=NULL;
				}
				if(unit->getFaction()->getType()->isHealthbarBackgroundTextureEnabled()) {
					healthbarBackgroundTexture=unit->getFaction()->getType()->getHealthbarBackgroundTexture();
					if(healthbarBackgroundTexture==NULL) {
						healthbarBackgroundTexture=coreData.getHealthbarBackgroundTexture();
					}
				} else {
					healthbarBackgroundTexture=NULL;
				}

				//replace them by the ones from the unit if existent
				if(unit->getType()->getHealthbarVisible()!=hbvOff && unit->getType()->getHealthbarVisible()!=hbvUndefined) {
					if(unit->getType()->getHealthbarHeight()!=-100.0f) {
						healthbarheight=unit->getType()->getHealthbarHeight();
					}
					if(unit->getType()->getHealthbarThickness()!=-1.0f) {
						healthbarthickness=unit->getType()->getHealthbarThickness();
					}
				}

				Vec3f currVec= unit->getCurrVectorFlat();
				if(healthbarheight==-100.0f) {
					currVec.y+=unit->getType()->getHeight();
				} else {
					currVec.y+=healthbarheight;
				}
				renderHealthBar(currVec,unit,healthbarthickness,healthbarLineBorder,healthbarTexture,healthbarBackgroundTexture);
			}
		}
	}
	glPopAttrib();
}

bool Renderer::isHealthBarVisible(const Unit *unit,int healthbarMode){
	int healthbarVisible=hbvUndefined;
	//check options (hotkey)
	if(healthbarMode==hbvUndefined) {
		healthbarVisible=unit->getFaction()->getType()->getHealthbarVisible();
	} else {
		healthbarVisible=healthbarMode;
	}

	//replace them by the ones from the unit if existent
	if(unit->getType()->getHealthbarVisible()!=hbvOff && unit->getType()->getHealthbarVisible()!=hbvUndefined) {
		if(healthbarMode==hbvUndefined) { //don't override the visible setting when hotkey is not hbvUndefined
			healthbarVisible=unit->getType()->getHealthbarVisible();
		}
	}

	bool settingsWantToRenderThem=!(healthbarVisible==hbvUndefined || (healthbarVisible&hbvOff))
			&& ((healthbarVisible&hbvAlways)
			|| ((healthbarVisible&hbvIfNeeded) && unit->getHp()<unit->getType()->getMaxHp()+unit->getTotalUpgrade()->getMaxHp())
			|| ((healthbarVisible&hbvIfNeeded) && unit->getType()->getMaxEp() > 0 && unit->getEp()<unit->getType()->getMaxEp()+unit->getTotalUpgrade()->getMaxEp())
			|| ((healthbarVisible&hbvIfNeeded) && unit->getProductionPercent() > 0)
			|| ((healthbarVisible&hbvSelected) && game->getGui()->isSelected(unit)));

	if(unit->isAlive() && (settingsWantToRenderThem)) {
		return true;
	}
	return false;
}

void Renderer::renderWaterEffects(){
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	const World *world= game->getWorld();
	const WaterEffects *we= world->getWaterEffects();
	const Map *map= world->getMap();
	CoreData &coreData= CoreData::getInstance();
	float height= map->getWaterLevel()+0.001f;

	assertGl();

	glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT);
	glEnable(GL_BLEND);
	glDisable(GL_ALPHA_TEST);
	glDepthMask(GL_FALSE);
	glDepthFunc(GL_LEQUAL);
	glEnable(GL_COLOR_MATERIAL);

	//glNormal3f(0.f, 1.f, 0.f);

	//splashes
	glBindTexture(GL_TEXTURE_2D, static_cast<Texture2DGl*>(coreData.getWaterSplashTexture())->getHandle());

	//!!!
	Vec2f texCoords[4];
	Vec3f vertices[4];
	Vec3f normals[4];

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

	for(int i=0; i<we->getWaterSplashCount(); ++i){
		const WaterSplash *ws= we->getWaterSplash(i);

		//render only if enabled
		if(ws->getEnabled()){

			//render only if visible
			Vec2i intPos= Vec2i(static_cast<int>(ws->getPos().x), static_cast<int>(ws->getPos().y));
			const Vec2i &mapPos = Map::toSurfCoords(intPos);

			bool visible = map->getSurfaceCell(mapPos)->isVisible(world->getThisTeamIndex());
			if(visible == false && world->showWorldForPlayer(world->getThisFactionIndex()) == true) {
				visible = true;
			}

			if(visible == true) {
				float scale= ws->getAnim()*ws->getSize();
				texCoords[0]		= Vec2f(0.f, 1.f);
				vertices[0]			= Vec3f(ws->getPos().x-scale, height, ws->getPos().y+scale);
				normals[0]			= Vec3f(0.f, 1.f, 0.f);

				texCoords[1]		= Vec2f(0.f, 0.f);
				vertices[1]			= Vec3f(ws->getPos().x-scale, height, ws->getPos().y-scale);
				normals[1]			= Vec3f(0.f, 1.f, 0.f);

				texCoords[2]		= Vec2f(1.f, 1.f);
				vertices[2]			= Vec3f(ws->getPos().x+scale, height, ws->getPos().y+scale);
				normals[2]			= Vec3f(0.f, 1.f, 0.f);

				texCoords[3]		= Vec2f(1.f, 0.f);
				vertices[3]			= Vec3f(ws->getPos().x+scale, height, ws->getPos().y-scale);
				normals[3]			= Vec3f(0.f, 1.f, 0.f);

				glColor4f(1.f, 1.f, 1.f, 1.f - ws->getAnim());
				glTexCoordPointer(2, GL_FLOAT, 0,&texCoords[0]);
				glVertexPointer(3, GL_FLOAT, 0, &vertices[0]);
				glNormalPointer(GL_FLOAT, 0, &normals[0]);
				glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);


/*
				glBegin(GL_TRIANGLE_STRIP);
					glTexCoord2f(0.f, 1.f);
					glVertex3f(ws->getPos().x-scale, height, ws->getPos().y+scale);
					glTexCoord2f(0.f, 0.f);
					glVertex3f(ws->getPos().x-scale, height, ws->getPos().y-scale);
					glTexCoord2f(1.f, 1.f);
					glVertex3f(ws->getPos().x+scale, height, ws->getPos().y+scale);
					glTexCoord2f(1.f, 0.f);
					glVertex3f(ws->getPos().x+scale, height, ws->getPos().y-scale);
				glEnd();
*/
			}
		}
	}

	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glDisableClientState(GL_NORMAL_ARRAY);
	glDisableClientState(GL_VERTEX_ARRAY);

	glPopAttrib();

	assertGl();
}

void Renderer::renderHud(){
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	Texture2D *hudTexture=game->getGui()->getHudTexture();
	if(hudTexture!=NULL){
		const Metrics &metrics= Metrics::getInstance();
		renderTextureQuad(0, 0, metrics.getVirtualW(), metrics.getVirtualH(),hudTexture,1.0f);
	}
}

void Renderer::renderMinimap(){
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

    const World *world= game->getWorld();
	const Minimap *minimap= world->getMinimap();

	if(minimap == NULL || minimap->getTexture() == NULL) {
		return;
	}

	const GameCamera *gameCamera= game->getGameCamera();
	const Pixmap2D *pixmap= minimap->getTexture()->getPixmapConst();
	const Metrics &metrics= Metrics::getInstance();
	const WaterEffects *attackEffects= world->getAttackEffects();

	int mx= metrics.getMinimapX();
	int my= metrics.getMinimapY();
	int mw= metrics.getMinimapW();
	int mh= metrics.getMinimapH();

	Vec2f zoom= Vec2f(
		static_cast<float>(mw)/ pixmap->getW(),
		static_cast<float>(mh)/ pixmap->getH());

	assertGl();

	// render minimap border
	Vec4f col= game->getGui()->getDisplay()->getColor();
	glColor4f(col.x*0.5f,col.y*0.5f,col.z*0.5f,1.0 );

	int borderWidth=2;
	glBegin(GL_QUADS);
	glVertex2i(mx-borderWidth, my-borderWidth);
	glVertex2i(mx-borderWidth, my);
	glVertex2i(mx+mw+borderWidth, my);
	glVertex2i(mx+mw+borderWidth, my-borderWidth);
	glEnd();

	glBegin(GL_QUADS);
	glVertex2i(mx-borderWidth, my+mh+borderWidth);
	glVertex2i(mx-borderWidth, my+mh);
	glVertex2i(mx+mw+borderWidth, my+mh);
	glVertex2i(mx+mw+borderWidth, my+mh+borderWidth);
	glEnd();

	glBegin(GL_QUADS);
	glVertex2i(mx-borderWidth, my);
	glVertex2i(mx-borderWidth, my+mh);
	glVertex2i(mx, my+mh);
	glVertex2i(mx, my);
	glEnd();

	glBegin(GL_QUADS);
	glVertex2i(mx+mw, my);
	glVertex2i(mx+mw, my+mh);
	glVertex2i(mx+mw+borderWidth, my+mh);
	glVertex2i(mx+mw+borderWidth, my);
	glEnd();

	assertGl();

	glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_LINE_BIT | GL_TEXTURE_BIT);

    //draw map
	glEnable(GL_TEXTURE_2D);
	glEnable(GL_BLEND);

	glActiveTexture(fowTexUnit);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, static_cast<const Texture2DGl*>(minimap->getFowTexture())->getHandle());
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_TEXTURE);

	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_ADD);
	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR);
	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_TEXTURE);

	glActiveTexture(baseTexUnit);
	glBindTexture(GL_TEXTURE_2D, static_cast<const Texture2DGl*>(minimap->getTexture())->getHandle());

	glColor4f(0.5f, 0.5f, 0.5f, 0.2f);

	glBegin(GL_TRIANGLE_STRIP);
		glTexCoord2f(0.0f, 1.0f);
		glMultiTexCoord2f(fowTexUnit, 0.0f, 1.0f);
		glVertex2i(mx, my);

		glTexCoord2f(0.0f, 0.0f);
		glMultiTexCoord2f(fowTexUnit, 0.0f, 0.0f);
		glVertex2i(mx, my+mh);

		glTexCoord2f(1.0f, 1.0f);
		glMultiTexCoord2f(fowTexUnit, 1.0f, 1.0f);
		glVertex2i(mx+mw, my);

		glTexCoord2f(1.0f, 0.0f);
		glMultiTexCoord2f(fowTexUnit, 1.0f, 0.0f);
		glVertex2i(mx+mw, my+mh);
	glEnd();

    glDisable(GL_BLEND);

	glActiveTexture(fowTexUnit);
	glDisable(GL_TEXTURE_2D);
	glActiveTexture(baseTexUnit);
	glDisable(GL_TEXTURE_2D);

	glEnable(GL_BLEND);

	const int itemCount = attackEffects->getWaterSplashCount() * 12;
	if(itemCount > 0) {
		vector<Vec2f> vertices;
		vertices.resize(itemCount);
		vector<Vec4f> colors;
		colors.resize(itemCount);

		// draw attack alarm
		int vertexIndex = 0;
		for(int i = 0; i < attackEffects->getWaterSplashCount(); ++i) {
			const WaterSplash *ws = attackEffects->getWaterSplash(i);
			float scale= (1/ws->getAnim()*ws->getSize())*5;
			//glColor4f(1.f, 1.f, 0.f, 1.f-ws->getAnim());
			float alpha=(1.f-ws->getAnim())*0.01f;
			Vec2f pos= ws->getPos()/Map::cellScale;
			float attackX=mx +pos.x*zoom.x;
			float attackY=my +mh -pos.y*zoom.y;
			if(ws->getEnabled()){
	//			glBegin(GL_QUADS);
	//				glVertex2f(attackX-scale, attackY-scale);
	//				glVertex2f(attackX-scale, attackY+scale);
	//				glVertex2f(attackX+scale, attackY+scale);
	//				glVertex2f(attackX+scale, attackY-scale);
	//			glEnd();

				colors[vertexIndex] = Vec4f(1.f, 1.f, 0.f, alpha);
				vertices[vertexIndex] = Vec2f(attackX-scale, attackY-scale);
				vertexIndex++;
				colors[vertexIndex] = Vec4f(1.f, 1.f, 0.f, alpha);
				vertices[vertexIndex] = Vec2f(attackX-scale, attackY+scale);
				vertexIndex++;
				colors[vertexIndex] = Vec4f(1.f, 1.f, 0.f, 0.8f);
				vertices[vertexIndex] = Vec2f(attackX, attackY);
				vertexIndex++;

				colors[vertexIndex] = Vec4f(1.f, 1.f, 0.f, alpha);
				vertices[vertexIndex] = Vec2f(attackX+scale, attackY+scale);
				vertexIndex++;
				colors[vertexIndex] = Vec4f(1.f, 1.f, 0.f, alpha);
				vertices[vertexIndex] = Vec2f(attackX-scale, attackY+scale);
				vertexIndex++;
				colors[vertexIndex] = Vec4f(1.f, 1.f, 0.f, 0.8f);
				vertices[vertexIndex] = Vec2f(attackX, attackY);
				vertexIndex++;

				colors[vertexIndex] = Vec4f(1.f, 1.f, 0.f, alpha);
				vertices[vertexIndex] = Vec2f(attackX+scale, attackY+scale);
				vertexIndex++;
				colors[vertexIndex] = Vec4f(1.f, 1.f, 0.f, alpha);
				vertices[vertexIndex] = Vec2f(attackX+scale, attackY-scale);
				vertexIndex++;
				colors[vertexIndex] = Vec4f(1.f, 1.f, 0.f, 0.8f);
				vertices[vertexIndex] = Vec2f(attackX, attackY);
				vertexIndex++;

				colors[vertexIndex] = Vec4f(1.f, 1.f, 0.f, alpha);
				vertices[vertexIndex] = Vec2f(attackX+scale, attackY-scale);
				vertexIndex++;
				colors[vertexIndex] = Vec4f(1.f, 1.f, 0.f, alpha);
				vertices[vertexIndex] = Vec2f(attackX-scale, attackY-scale);
				vertexIndex++;
				colors[vertexIndex] = Vec4f(1.f, 1.f, 0.f, 0.8f);
				vertices[vertexIndex] = Vec2f(attackX, attackY);
				vertexIndex++;

	/*
				glBegin(GL_TRIANGLES);
					glColor4f(1.f, 1.f, 0.f, alpha);
					glVertex2f(attackX-scale, attackY-scale);
					glVertex2f(attackX-scale, attackY+scale);
					glColor4f(1.f, 1.f, 0.f, 0.8f);
					glVertex2f(attackX, attackY);
				glEnd();
				glBegin(GL_TRIANGLES);
					glColor4f(1.f, 1.f, 0.f, alpha);
					glVertex2f(attackX-scale, attackY+scale);
					glVertex2f(attackX+scale, attackY+scale);
					glColor4f(1.f, 1.f, 0.f, 0.8f);
					glVertex2f(attackX, attackY);
				glEnd();
				glBegin(GL_TRIANGLES);
					glColor4f(1.f, 1.f, 0.f, alpha);
					glVertex2f(attackX+scale, attackY+scale);
					glVertex2f(attackX+scale, attackY-scale);
					glColor4f(1.f, 1.f, 0.f, 0.8f);
					glVertex2f(attackX, attackY);
				glEnd();
				glBegin(GL_TRIANGLES);
					glColor4f(1.f, 1.f, 0.f, alpha);
					glVertex2f(attackX+scale, attackY-scale);
					glVertex2f(attackX-scale, attackY-scale);
					glColor4f(1.f, 1.f, 0.f, 0.8f);
					glVertex2f(attackX, attackY);
				glEnd();
	*/

			}
		}

		if(vertexIndex > 0) {
			glEnableClientState(GL_COLOR_ARRAY);
			glEnableClientState(GL_VERTEX_ARRAY);
			glColorPointer(4,GL_FLOAT, 0, &colors[0]);
			glVertexPointer(2, GL_FLOAT, 0, &vertices[0]);
			glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexIndex);
			glDisableClientState(GL_COLOR_ARRAY);
			glDisableClientState(GL_VERTEX_ARRAY);
		}
	}

    glDisable(GL_BLEND);

	//draw units
	VisibleQuadContainerCache &qCache = getQuadCache();
	std::vector<Unit *> visibleUnitList = qCache.visibleUnitList;

	const bool showAllUnitsInMinimap = Config::getInstance().getBool("DebugGameSynchUI","false");
	if(showAllUnitsInMinimap == true) {
		visibleUnitList.clear();

		const World *world= game->getWorld();
		for(unsigned int i = 0; i < (unsigned int)world->getFactionCount(); ++i) {
			const Faction *faction = world->getFaction(i);
			for(unsigned int j = 0; j < (unsigned int)faction->getUnitCount(); ++j) {
				Unit *unit = faction->getUnit(j);
				visibleUnitList.push_back(unit);
			}
		}
	}

	if(visibleUnitList.empty() == false) {
		uint32 unitIdx=0;
		vector<Vec2f> unit_vertices;
		unit_vertices.resize(visibleUnitList.size()*4);
		vector<Vec3f> unit_colors;
		unit_colors.resize(visibleUnitList.size()*4);

		for(int visibleIndex = 0;
				visibleIndex < (int)visibleUnitList.size(); ++visibleIndex) {
			Unit *unit = visibleUnitList[visibleIndex];
			if (unit->isAlive() == false) {
				continue;
			}

			Vec2i pos= unit->getPos() / Map::cellScale;
			int size= unit->getType()->getSize();
			Vec3f color=  unit->getFaction()->getTexture()->getPixmapConst()->getPixel3f(0, 0);

			unit_colors[unitIdx] = color;
			unit_vertices[unitIdx] = Vec2f(mx + pos.x*zoom.x, my + mh - (pos.y*zoom.y));
			unitIdx++;

			unit_colors[unitIdx] = color;
			unit_vertices[unitIdx] = Vec2f(mx + (pos.x+1)*zoom.x+size, my + mh - (pos.y*zoom.y));
			unitIdx++;

			unit_colors[unitIdx] = color;
			unit_vertices[unitIdx] = Vec2f(mx + (pos.x+1)*zoom.x+size, my + mh - ((pos.y+size)*zoom.y));
			unitIdx++;

			unit_colors[unitIdx] = color;
			unit_vertices[unitIdx] = Vec2f(mx + pos.x*zoom.x, my + mh - ((pos.y+size)*zoom.y));
			unitIdx++;
		}

		if(unitIdx > 0) {
			glEnableClientState(GL_COLOR_ARRAY);
			glEnableClientState(GL_VERTEX_ARRAY);
			glColorPointer(3,GL_FLOAT, 0, &unit_colors[0]);
			glVertexPointer(2, GL_FLOAT, 0, &unit_vertices[0]);
			glDrawArrays(GL_QUADS, 0, unitIdx);
			glDisableClientState(GL_COLOR_ARRAY);
			glDisableClientState(GL_VERTEX_ARRAY);
		}

	}

	renderMarkedCellsOnMinimap();

    //draw camera
	float wRatio= static_cast<float>(metrics.getMinimapW()) / world->getMap()->getW();
	float hRatio= static_cast<float>(metrics.getMinimapH()) / world->getMap()->getH();

    int x= static_cast<int>(gameCamera->getPos().x * wRatio);
    int y= static_cast<int>(gameCamera->getPos().z * hRatio);

    float ang= degToRad(gameCamera->getHAng());

    glEnable(GL_BLEND);

    int x1;
    int y1;
    x1 = mx + x + static_cast<int>(20*std::sin(ang-pi/5));
    y1 = my + mh - (y-static_cast<int>(20*std::cos(ang-pi/5)));

    int x2;
    int y2;
    x2 = mx + x + static_cast<int>(20*std::sin(ang+pi/5));
    y2 = my + mh - (y-static_cast<int>(20*std::cos(ang+pi/5)));

    glColor4f(1.f, 1.f, 1.f, 1.f);
    glBegin(GL_TRIANGLES);

	glVertex2i(mx+x, my+mh-y);
	glColor4f(1.f, 1.f, 1.f, 0.0f);
	glVertex2i(x1,y1);
	glColor4f(1.f, 1.f, 1.f, 0.0f);
    glVertex2i(x2,y2);

    glEnd();

    glPopAttrib();

	assertGl();
}

void Renderer::renderHighlightedCellsOnMinimap() {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}
	// Draw marked cells
	const std::vector<MarkedCell> *highlightedCells = game->getHighlightedCells();
	if(highlightedCells->empty() == false) {
		//const Map *map= game->getWorld()->getMap();
	    const World *world= game->getWorld();
		const Minimap *minimap= world->getMinimap();
		int pointersize=10;
		if(minimap == NULL || minimap->getTexture() == NULL) {
			return;
		}

		//const GameCamera *gameCamera= game->getGameCamera();
		const Pixmap2D *pixmap= minimap->getTexture()->getPixmapConst();
		const Metrics &metrics= Metrics::getInstance();


		//int mx= metrics.getMinimapX();
		int my= metrics.getMinimapY();
		int mw= metrics.getMinimapW();
		int mh= metrics.getMinimapH();

		Vec2f zoom= Vec2f(
			static_cast<float>(mw)/ pixmap->getW()/2,
			static_cast<float>(mh)/ pixmap->getH()/2);

		for(int i = 0;i < (int)highlightedCells->size(); i++) {
			const MarkedCell *mc=&highlightedCells->at(i);
			if(mc->getFaction() == NULL || (mc->getFaction()->getTeam() == game->getWorld()->getThisFaction()->getTeam())) {
				const Texture2D *texture= game->getHighlightCellTexture();
				Vec3f color(MarkedCell::static_system_marker_color);
				if(mc->getFaction() != NULL) {
					color=  mc->getFaction()->getTexture()->getPixmapConst()->getPixel3f(0, 0);
				}
				int lighting=(mc->getAliveCount()%15);
				Vec3f myColor=Vec3f(color.x/2+.5f/lighting,color.y/2+.5f/lighting,color.z/2+.5f/lighting);

				Vec2i pos=mc->getTargetPos();
				if(texture != NULL) {
					//float alpha = 0.49f+0.5f/(mc->getAliveCount()%15);
					float alpha=1.0f;
					renderTextureQuad((int)(pos.x*zoom.x)+pointersize, my + mh-(int)(pos.y*zoom.y), pointersize, pointersize, texture, alpha,&myColor);
				}
			}
		}
	}
}

void Renderer::renderMarkedCellsOnMinimap() {
	// Draw marked cells
	std::map<Vec2i, MarkedCell> markedCells = game->getMapMarkedCellList();
	if(markedCells.empty() == false) {
		//const Map *map= game->getWorld()->getMap();
	    const World *world= game->getWorld();
		const Minimap *minimap= world->getMinimap();

		if(minimap == NULL || minimap->getTexture() == NULL) {
			return;
		}

		//const GameCamera *gameCamera= game->getGameCamera();
		const Pixmap2D *pixmap= minimap->getTexture()->getPixmapConst();
		const Metrics &metrics= Metrics::getInstance();
		//const WaterEffects *attackEffects= world->getAttackEffects();

		int mx= metrics.getMinimapX();
		int my= metrics.getMinimapY();
		int mw= metrics.getMinimapW();
		int mh= metrics.getMinimapH();

		Vec2f zoom= Vec2f(
			static_cast<float>(mw)/ pixmap->getW(),
			static_cast<float>(mh)/ pixmap->getH());

		uint32 unitIdx=0;
		vector<Vec2f> unit_vertices;
		unit_vertices.resize(markedCells.size()*4);
		vector<Vec4f> unit_colors;
		unit_colors.resize(markedCells.size()*4);

		for(std::map<Vec2i, MarkedCell>::iterator iterMap =markedCells.begin();
				iterMap != markedCells.end(); ++iterMap) {
			MarkedCell &bm = iterMap->second;
			if(bm.getPlayerIndex() < 0 ||
				(bm.getFaction() != NULL &&
				bm.getFaction()->getTeam() == game->getWorld()->getThisFaction()->getTeam())) {
				Vec2i pos= bm.getTargetPos() / Map::cellScale;
				float size= 0.5f;

				Vec3f color(MarkedCell::static_system_marker_color);
				if(bm.getFaction() != NULL) {
					color=  bm.getFaction()->getTexture()->getPixmapConst()->getPixel3f(0, 0);
				}
				float alpha = 0.65f;

				unit_colors[unitIdx] = Vec4f(color.x,color.y,color.z,alpha);
				unit_vertices[unitIdx] = Vec2f(mx + pos.x*zoom.x, my + mh - (pos.y*zoom.y));
				unitIdx++;

				unit_colors[unitIdx] = Vec4f(color.x,color.y,color.z,alpha);
				unit_vertices[unitIdx] = Vec2f(mx + (pos.x+1)*zoom.x+size, my + mh - (pos.y*zoom.y));
				unitIdx++;

				unit_colors[unitIdx] = Vec4f(color.x,color.y,color.z,alpha);
				unit_vertices[unitIdx] = Vec2f(mx + (pos.x+1)*zoom.x+size, my + mh - ((pos.y+size)*zoom.y));
				unitIdx++;

				unit_colors[unitIdx] = Vec4f(color.x,color.y,color.z,alpha);
				unit_vertices[unitIdx] = Vec2f(mx + pos.x*zoom.x, my + mh - ((pos.y+size)*zoom.y));
				unitIdx++;
			}
		}

		if(unitIdx > 0) {
			glEnable(GL_BLEND);

			glEnableClientState(GL_COLOR_ARRAY);
			glEnableClientState(GL_VERTEX_ARRAY);
			glColorPointer(4,GL_FLOAT, 0, &unit_colors[0]);
			glVertexPointer(2, GL_FLOAT, 0, &unit_vertices[0]);
			glDrawArrays(GL_QUADS, 0, unitIdx);
			//glDrawArrays(GL_TRIANGLE_STRIP, 0, unitIdx);
			glDisableClientState(GL_COLOR_ARRAY);
			glDisableClientState(GL_VERTEX_ARRAY);

			glDisable(GL_BLEND);
		}
	}
}
void Renderer::renderVisibleMarkedCells(bool renderTextHint,int x, int y) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	// Draw marked cells
	std::map<Vec2i, MarkedCell> markedCells = game->getMapMarkedCellList();
	if(markedCells.empty() == false) {
		const Texture2D *texture= game->getMarkCellTexture();
		const int yOffset = -40;

		for(std::map<Vec2i, MarkedCell>::iterator iterMap =markedCells.begin();
				iterMap != markedCells.end(); ++iterMap) {
			MarkedCell &bm = iterMap->second;
			if(bm.getPlayerIndex() < 0 ||
				(bm.getFaction() != NULL &&
				 bm.getFaction()->getTeam() == game->getWorld()->getThisFaction()->getTeam())) {
				const Map *map= game->getWorld()->getMap();
				std::pair<bool,Vec3f> bmVisible = posInCellQuadCache(
						map->toSurfCoords(bm.getTargetPos()));
				if(bmVisible.first == true) {
					if(renderTextHint == true) {
						if(bm.getNote() != "") {
							bool validPosObjWorld= x > bmVisible.second.x &&
													y > bmVisible.second.y + yOffset &&
													x < bmVisible.second.x + texture->getTextureWidth() &&
													y < bmVisible.second.y + yOffset + texture->getTextureHeight();

							if(validPosObjWorld) {
								//printf("Checking for hint text render mouse [%d,%d] marker pos [%d,%d] validPosObjWorld = %d, hint [%s]\n",x,y,bm.getTargetPos().x,bm.getTargetPos().y,validPosObjWorld,bm.getNote().c_str());

								//Lang &lang= Lang::getInstance();
								Vec4f fontColor = Vec4f(1.0f, 1.0f, 1.0f, 0.25f);

								if(renderText3DEnabled == true) {
									renderTextShadow3D(bm.getNote(), CoreData::getInstance().getConsoleFont3D(), fontColor,
											bmVisible.second.x, bmVisible.second.y);
								}
								else {
									renderTextShadow(bm.getNote(), CoreData::getInstance().getConsoleFont(), fontColor,
											bmVisible.second.x, bmVisible.second.y);
								}
							}
						}
					}
					else {


/*
						//texture 0
						glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

						//set color to interpolation
						glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);

						glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
						glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);

						glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE1);
						glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);

						glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_TEXTURE);
						glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);

						//set alpha to 1
						glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
						glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
						glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);

						//texture 1
						glActiveTexture(GL_TEXTURE1);
						glMultiTexCoord2f(GL_TEXTURE1, 0.f, 0.f);
						glEnable(GL_TEXTURE_2D);

						glBindTexture(GL_TEXTURE_2D, static_cast<const Texture2DGl*>(bm.getFaction()->getTexture())->getHandle());
						glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

						glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);

						glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR);
						glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);

						glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS);
						glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);

						//set alpha to 1
						glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
						glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR);
						glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);

						glActiveTexture(GL_TEXTURE0);
*/

						glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
						glEnable(GL_BLEND);
						glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

						Vec3f color(MarkedCell::static_system_marker_color);
						if(bm.getFaction() != NULL) {
							color = bm.getFaction()->getTexture()->getPixmapConst()->getPixel3f(0,0);
						}

						renderTextureQuad(
								bmVisible.second.x,bmVisible.second.y + yOffset,
								texture->getTextureWidth(),texture->getTextureHeight(),texture,0.8f,&color);

/*
						glActiveTexture(GL_TEXTURE1);
						glDisable(GL_TEXTURE_2D);
						glActiveTexture(GL_TEXTURE0);
						glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
*/
					}
				}
			}
		}
	}
}

void Renderer::renderDisplay() {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	CoreData &coreData= CoreData::getInstance();
	const Metrics &metrics= Metrics::getInstance();
	const Display *display= game->getGui()->getDisplay();

	glPushAttrib(GL_ENABLE_BIT);

	if(renderText3DEnabled == true) {
		//infoString
		renderTextShadow3D(
			display->getInfoText().c_str(),
			coreData.getDisplayFont3D(),
			display->getColor(),
			metrics.getDisplayX(),
			metrics.getDisplayY()+Display::infoStringY);

		//title
		renderTextShadow3D(
			display->getTitle().c_str(),
			coreData.getDisplayFont3D(),
			display->getColor(),
			metrics.getDisplayX()+40,
			metrics.getDisplayY() + metrics.getDisplayH() - 20);

		glColor3f(0.0f, 0.0f, 0.0f);

		//text
		renderTextShadow3D(
			display->getText().c_str(),
			coreData.getDisplayFont3D(),
			display->getColor(),
			metrics.getDisplayX() -1,
			metrics.getDisplayY() + metrics.getDisplayH() - 56);

		//progress Bar
		if(display->getProgressBar() != -1) {
			renderProgressBar3D(
				display->getProgressBar(),
				metrics.getDisplayX(),
				metrics.getDisplayY() + metrics.getDisplayH()-50,
				coreData.getDisplayFontSmall3D(), 175);
		}
	}
	else {
		//infoString
		renderTextShadow(
			display->getInfoText().c_str(),
			coreData.getDisplayFont(),
			display->getColor(),
			metrics.getDisplayX(),
			metrics.getDisplayY()+Display::infoStringY);

		//title
		renderTextShadow(
			display->getTitle().c_str(),
			coreData.getDisplayFont(),
			display->getColor(),
			metrics.getDisplayX()+40,
			metrics.getDisplayY() + metrics.getDisplayH() - 20);

		glColor3f(0.0f, 0.0f, 0.0f);

		//text
		renderTextShadow(
			display->getText().c_str(),
			coreData.getDisplayFont(),
			display->getColor(),
			metrics.getDisplayX() -1,
			metrics.getDisplayY() + metrics.getDisplayH() - 56);

		//progress Bar
		if(display->getProgressBar()!=-1){
			renderProgressBar(
				display->getProgressBar(),
				metrics.getDisplayX(),
				metrics.getDisplayY() + metrics.getDisplayH()-50,
				coreData.getDisplayFontSmall());
		}
	}

	//up images
	glEnable(GL_TEXTURE_2D);

	glColor3f(1.f, 1.f, 1.f);
	for(int i=0; i<Display::upCellCount; ++i){
		if(display->getUpImage(i)!=NULL){
			renderQuad(
				metrics.getDisplayX()+display->computeUpX(i),
				metrics.getDisplayY()+display->computeUpY(i),
				display->getUpImageSize(), display->getUpImageSize(), display->getUpImage(i));
		}
	}

 	//down images
	for(int i=0; i<Display::downCellCount; ++i){
		if(display->getDownImage(i)!=NULL){
			if(display->getDownLighted(i)){
                glColor3f(1.f, 1.f, 1.f);
			}
			else{
                glColor3f(0.3f, 0.3f, 0.3f);
			}

			int x= metrics.getDisplayX()+display->computeDownX(i);
			int y= metrics.getDisplayY()+display->computeDownY(i);
			int size= Display::imageSize;

			if(display->getDownSelectedPos()==i){
				x-= 3;
				y-= 3;
				size+= 6;
			}

			renderQuad(x, y, size, size, display->getDownImage(i));
		}
	}

	//selection
	int downPos= display->getDownSelectedPos();
	if(downPos!=Display::invalidPos){
		const Texture2D *texture= display->getDownImage(downPos);
		if(texture!=NULL){
			int x= metrics.getDisplayX()+display->computeDownX(downPos)-3;
			int y= metrics.getDisplayY()+display->computeDownY(downPos)-3;
			int size= Display::imageSize+6;
			renderQuad(x, y, size, size, display->getDownImage(downPos));
		}
    }

	glPopAttrib();
}

void Renderer::renderMenuBackground(const MenuBackground *menuBackground) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	assertGl();

	const Vec3f &cameraPosition= menuBackground->getCamera()->getConstPosition();

	glPushAttrib(GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT);

	//clear
	Vec4f fogColor= Vec4f(0.4f, 0.4f, 0.4f, 1.f) * menuBackground->getFade();
	glClearColor(fogColor.x, fogColor.y, fogColor.z, 1.f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glFogfv(GL_FOG_COLOR, fogColor.ptr());

	//light
	Vec4f lightPos= Vec4f(10.f, 10.f, 10.f, 1.f)* menuBackground->getFade();
	Vec4f diffLight= Vec4f(0.9f, 0.9f, 0.9f, 1.f)* menuBackground->getFade();
	Vec4f ambLight= Vec4f(0.3f, 0.3f, 0.3f, 1.f)* menuBackground->getFade();
	Vec4f specLight= Vec4f(0.1f, 0.1f, 0.1f, 1.f)* menuBackground->getFade();

	glEnable(GL_LIGHT0);
	glLightfv(GL_LIGHT0, GL_POSITION, lightPos.ptr());
	glLightfv(GL_LIGHT0, GL_DIFFUSE, diffLight.ptr());
	glLightfv(GL_LIGHT0, GL_AMBIENT, ambLight.ptr());
	glLightfv(GL_LIGHT0, GL_SPECULAR, specLight.ptr());

	//main model
	glEnable(GL_ALPHA_TEST);
	glAlphaFunc(GL_GREATER, 0.5f);
	modelRenderer->begin(true, true, true, false);
	menuBackground->getMainModelPtr()->updateInterpolationData(menuBackground->getAnim(), true);
	modelRenderer->render(menuBackground->getMainModelPtr());
	modelRenderer->end();
	glDisable(GL_ALPHA_TEST);

	//characters
	float dist= menuBackground->getAboutPosition().dist(cameraPosition);
	float minDist= 3.f;
	if(dist < minDist) {

		glAlphaFunc(GL_GREATER, 0.0f);
		float alpha= clamp((minDist-dist) / minDist, 0.f, 1.f);
		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Vec4f(1.0f, 1.0f, 1.0f, alpha).ptr());

		std::vector<Vec3f> &characterMenuScreenPositionListCache =
				CacheManager::getCachedItem< std::vector<Vec3f> >(GameConstants::characterMenuScreenPositionListCacheLookupKey);
		characterMenuScreenPositionListCache.clear();

		modelRenderer->begin(true, true, false, false);

		for(int i=0; i < MenuBackground::characterCount; ++i) {
			glMatrixMode(GL_MODELVIEW);
			glPushMatrix();
			glLoadIdentity();

			Vec3f worldPos(i*2.f-4.f, -1.4f, -7.5f);
			glTranslatef(worldPos.x,worldPos.y,worldPos.z);

			//
			// Get the screen coordinates for each character model - START
			//std::vector<GLdouble> projection(16);
			//std::vector<GLdouble> modelview(16);
			//std::vector<GLdouble> screen_coords(3);

			//glGetDoublev(GL_PROJECTION_MATRIX, projection.data());
			//glGetDoublev(GL_MODELVIEW_MATRIX, modelview.data());

			const Metrics &metrics= Metrics::getInstance();
			GLint viewport[]= {0, 0, metrics.getVirtualW(), metrics.getVirtualH()};

			//get matrices
			GLdouble projection[16];
			glGetDoublev(GL_PROJECTION_MATRIX, projection);

			GLdouble modelview[16];
			glGetDoublev(GL_MODELVIEW_MATRIX, modelview);

			//get the screen coordinates
			GLdouble screen_coords[3];

			gluProject(worldPos.x, worldPos.y, worldPos.z,
			    modelview, projection, viewport,
			    &screen_coords[0], &screen_coords[1], &screen_coords[2]);
			characterMenuScreenPositionListCache.push_back(Vec3f(screen_coords[0],screen_coords[1],screen_coords[2]));
			// Get the screen coordinates for each character model - END
			//

			menuBackground->getCharacterModelPtr(i)->updateInterpolationData(menuBackground->getAnim(), true);
			modelRenderer->render(menuBackground->getCharacterModelPtr(i));

			glPopMatrix();
		}
		modelRenderer->end();
	}

	//water
	if(menuBackground->getWater()) {

		//water surface
		const int waterTesselation= 10;
		const int waterSize= 250;
		const int waterQuadSize= 2*waterSize/waterTesselation;
		const float waterHeight= menuBackground->getWaterHeight();

		glEnable(GL_BLEND);

		glNormal3f(0.f, 1.f, 0.f);
		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Vec4f(1.f, 1.f, 1.f, 1.f).ptr());
		GLuint waterHandle= static_cast<Texture2DGl*>(menuBackground->getWaterTexture())->getHandle();
		glBindTexture(GL_TEXTURE_2D, waterHandle);
		for(int i=1; i < waterTesselation; ++i) {
			glBegin(GL_TRIANGLE_STRIP);
			for(int j=1; j < waterTesselation; ++j) {
				glTexCoord2i(1, 2 % j);
				glVertex3f(-waterSize+i*waterQuadSize, waterHeight, -waterSize+j*waterQuadSize);
				glTexCoord2i(0, 2 % j);
				glVertex3f(-waterSize+(i+1)*waterQuadSize, waterHeight, -waterSize+j*waterQuadSize);
			}
			glEnd();
		}
		glDisable(GL_BLEND);

		//raindrops
		if(menuBackground->getRain()) {
			const float maxRaindropAlpha= 0.5f;

			glEnable(GL_BLEND);
			glDisable(GL_LIGHTING);
			glDisable(GL_ALPHA_TEST);
			glDepthMask(GL_FALSE);

			//splashes
			CoreData &coreData= CoreData::getInstance();
			glBindTexture(GL_TEXTURE_2D, static_cast<Texture2DGl*>(coreData.getWaterSplashTexture())->getHandle());
			for(int i=0; i< MenuBackground::raindropCount; ++i) {

				Vec2f pos= menuBackground->getRaindropPos(i);
				float scale= menuBackground->getRaindropState(i);
				float alpha= maxRaindropAlpha-scale*maxRaindropAlpha;

				glMatrixMode(GL_MODELVIEW);
				glPushMatrix();

				glColor4f(1.f, 1.f, 1.f, alpha);
				glTranslatef(pos.x, waterHeight+0.01f, pos.y);

				glBegin(GL_TRIANGLE_STRIP);
					glTexCoord2f(0.f, 1.f);
					glVertex3f(-scale, 0, scale);
					glTexCoord2f(0.f, 0.f);
					glVertex3f(-scale, 0, -scale);
					glTexCoord2f(1.f, 1.f);
					glVertex3f(scale, 0, scale);
					glTexCoord2f(1.f, 0.f);
					glVertex3f(scale, 0, -scale);
				glEnd();

				glPopMatrix();
			}
		}
	}

	glPopAttrib();

	assertGl();
}

void Renderer::renderMenuBackground(Camera *camera, float fade, Model *mainModel, vector<Model *> characterModels,const Vec3f characterPosition, float anim) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	assertGl();

	const Vec3f &cameraPosition= camera->getConstPosition();

	glPushAttrib(GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT);

	//clear
	//Vec4f fogColor= Vec4f(0.4f, 0.4f, 0.4f, 1.f) * fade;
	// Show black bacground
	Vec4f fogColor= Vec4f(0.f, 0.f, 0.f, 1.f);
	glClearColor(fogColor.x, fogColor.y, fogColor.z, 1.f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glFogfv(GL_FOG_COLOR, fogColor.ptr());

	//light
	Vec4f lightPos= Vec4f(10.f, 10.f, 10.f, 1.f)  * fade;
	Vec4f diffLight= Vec4f(0.9f, 0.9f, 0.9f, 1.f) * fade;
	Vec4f ambLight= Vec4f(0.3f, 0.3f, 0.3f, 1.f)  * fade;
	Vec4f specLight= Vec4f(0.1f, 0.1f, 0.1f, 1.f) * fade;

	glEnable(GL_LIGHT0);
	glLightfv(GL_LIGHT0, GL_POSITION, lightPos.ptr());
	glLightfv(GL_LIGHT0, GL_DIFFUSE, diffLight.ptr());
	glLightfv(GL_LIGHT0, GL_AMBIENT, ambLight.ptr());
	glLightfv(GL_LIGHT0, GL_SPECULAR, specLight.ptr());

	//main model
	if(mainModel) {
		glEnable(GL_ALPHA_TEST);
		glAlphaFunc(GL_GREATER, 0.5f);
		modelRenderer->begin(true, true, true, false);
		mainModel->updateInterpolationData(anim, true);
		modelRenderer->render(mainModel);
		modelRenderer->end();
		glDisable(GL_ALPHA_TEST);
	}

	//characters
	if(characterModels.empty() == false) {
		float dist= characterPosition.dist(cameraPosition);
		float minDist= 3.f;
		if(dist < minDist) {
			glAlphaFunc(GL_GREATER, 0.0f);
			float alpha= clamp((minDist-dist) / minDist, 0.f, 1.f);
			glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Vec4f(1.0f, 1.0f, 1.0f, alpha).ptr());
			modelRenderer->begin(true, true, false, false);

			for(unsigned int i = 0; i < characterModels.size(); ++i) {
				if(characterModels[i]) {
					glMatrixMode(GL_MODELVIEW);
					glPushMatrix();
					glLoadIdentity();
					glTranslatef(i*2.f-4.f, -1.4f, -7.5f);
					characterModels[i]->updateInterpolationData(anim, true);
					modelRenderer->render(characterModels[i]);
					glPopMatrix();
				}
			}
			modelRenderer->end();
		}
	}


	glPopAttrib();

	assertGl();
}

// ==================== computing ====================

bool Renderer::ccomputePosition(const Vec2i &screenPos, Vec2i &worldPos, bool exactCoords) {
	assertGl();
	const Map* map= game->getWorld()->getMap();
	const Metrics &metrics= Metrics::getInstance();
	float depth= 0.0f;
	GLdouble modelviewMatrix[16];
	GLdouble projectionMatrix[16];
	GLint viewport[4]= {0, 0, metrics.getScreenW(), metrics.getScreenH()};
	GLdouble worldX;
	GLdouble worldY;
	GLdouble worldZ;
	GLint screenX= (screenPos.x * metrics.getScreenW() / metrics.getVirtualW());
	GLint screenY= (screenPos.y * metrics.getScreenH() / metrics.getVirtualH());

	//get the depth in the cursor pixel
	glReadPixels(screenX, screenY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);

	//load matrices
	loadProjectionMatrix();
    loadGameCameraMatrix();

	//get matrices
	glGetDoublev(GL_MODELVIEW_MATRIX, modelviewMatrix);
	glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix);

	//get the world coordinates
	gluUnProject(
		screenX, screenY, depth,
		modelviewMatrix, projectionMatrix, viewport,
		&worldX, &worldY, &worldZ);

	//conver coords to int
	if(exactCoords == true) {
		worldPos= Vec2i(static_cast<int>(worldX), static_cast<int>(worldZ));
	}
	else {
		worldPos= Vec2i(static_cast<int>(worldX+0.5f), static_cast<int>(worldZ+0.5f));
	}

	//clamp coords to map size
	return map->isInside(worldPos);
}

// This method takes world co-ordinates and translates them to screen co-ords
Vec3f Renderer::computeScreenPosition(const Vec3f &worldPos) {
	if(worldToScreenPosCache.find(worldPos) != worldToScreenPosCache.end()) {
		return worldToScreenPosCache[worldPos];
	}
	assertGl();

	const Metrics &metrics= Metrics::getInstance();
	GLint viewport[]= {0, 0, metrics.getVirtualW(), metrics.getVirtualH()};
	GLdouble worldX = worldPos.x;
	GLdouble worldY = worldPos.y;
	GLdouble worldZ = worldPos.z;

	//load matrices
	loadProjectionMatrix();
    loadGameCameraMatrix();

	//get matrices
	GLdouble modelviewMatrix[16];
	glGetDoublev(GL_MODELVIEW_MATRIX, modelviewMatrix);
	GLdouble projectionMatrix[16];
	glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix);

	//get the screen coordinates
	GLdouble screenX;
	GLdouble screenY;
	GLdouble screenZ;
	gluProject(worldX, worldY, worldZ,
		modelviewMatrix, projectionMatrix, viewport,
		&screenX, &screenY, &screenZ);

	Vec3f screenPos(screenX,screenY,screenZ);
	worldToScreenPosCache[worldPos]=screenPos;

	return screenPos;
}

void Renderer::computeSelected(	Selection::UnitContainer &units, const Object *&obj,
								const bool withObjectSelection,
								const Vec2i &posDown, const Vec2i &posUp) {
	const string selectionType=toLower(Config::getInstance().getString("SelectionType",Config::colorPicking));

	if(selectionType==Config::colorPicking) {
		selectUsingColorPicking(units,obj, withObjectSelection,posDown, posUp);
	}
	/// Frustum approach --> Currently not accurate enough
	else if(selectionType==Config::frustumPicking) {
		selectUsingFrustumSelection(units,obj, withObjectSelection,posDown, posUp);
	}
	else {
		selectUsingSelectionBuffer(units,obj, withObjectSelection,posDown, posUp);
	}
}

void Renderer::selectUsingFrustumSelection(Selection::UnitContainer &units,
		const Object *&obj, const bool withObjectSelection,
		const Vec2i &posDown, const Vec2i &posUp) {
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();

	const Metrics &metrics= Metrics::getInstance();
	GLint view[]= {0, 0, metrics.getVirtualW(), metrics.getVirtualH()};

	//compute center and dimensions of selection rectangle
	int x = (posDown.x+posUp.x) / 2;
	int y = (posDown.y+posUp.y) / 2;
	int w = abs(posDown.x-posUp.x);
	int h = abs(posDown.y-posUp.y);
	if(w < 2) {
		w = 2;
	}
	if(h < 2) {
		h = 2;
	}

	gluPickMatrix(x, y, w, h, view);
	gluPerspective(perspFov, metrics.getAspectRatio(), perspNearPlane, perspFarPlane);
	loadGameCameraMatrix();

	VisibleQuadContainerCache quadSelectionCacheItem;
	ExtractFrustum(quadSelectionCacheItem);

	//pop matrices
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();

	VisibleQuadContainerCache &qCache = getQuadCache();
	if(qCache.visibleQuadUnitList.empty() == false) {
		for(int visibleUnitIndex = 0;
				visibleUnitIndex < (int)qCache.visibleQuadUnitList.size(); ++visibleUnitIndex) {
			Unit *unit = qCache.visibleQuadUnitList[visibleUnitIndex];
			if(unit != NULL && unit->isAlive()) {
				Vec3f unitPos = unit->getCurrMidHeightVector();
				bool insideQuad = CubeInFrustum(quadSelectionCacheItem.frustumData,
						unitPos.x, unitPos.y, unitPos.z, unit->getType()->getRenderSize());
				if(insideQuad == true) {
					units.push_back(unit);
				}
			}
		}
	}

	if(withObjectSelection == true) {
		if(qCache.visibleObjectList.empty() == false) {
			for(int visibleIndex = 0;
					visibleIndex < (int)qCache.visibleObjectList.size(); ++visibleIndex) {
				Object *object = qCache.visibleObjectList[visibleIndex];
				if(object != NULL) {
					bool insideQuad = CubeInFrustum(quadSelectionCacheItem.frustumData,
							object->getPos().x, object->getPos().y, object->getPos().z, 1);
					if(insideQuad == true) {
						obj = object;
						if(withObjectSelection == true) {
							break;
						}
					}
				}
			}
		}
	}
}

void Renderer::selectUsingSelectionBuffer(Selection::UnitContainer &units,
		const Object *&obj, const bool withObjectSelection,
		const Vec2i &posDown, const Vec2i &posUp) {
	//compute center and dimensions of selection rectangle
	int x = (posDown.x+posUp.x) / 2;
	int y = (posDown.y+posUp.y) / 2;
	int w = abs(posDown.x-posUp.x);
	int h = abs(posDown.y-posUp.y);
	if(w < 2) {
		w = 2;
	}
	if(h < 2) {
		h = 2;
	}

	//declarations
	GLuint selectBuffer[Gui::maxSelBuff];

	//setup matrices
	glSelectBuffer(Gui::maxSelBuff, selectBuffer);
	//glMatrixMode(GL_PROJECTION);
	//glPushMatrix();

	GLint renderModeResult = glRenderMode(GL_SELECT);
	if(renderModeResult < 0) {
		const char *errorString= reinterpret_cast<const char*>(gluErrorString(renderModeResult));
		char szBuf[8096]="";
		snprintf(szBuf,8096,"OpenGL error #%d [0x%X] : [%s] at file: [%s], line: %d",renderModeResult,renderModeResult,errorString,extractFileFromDirectoryPath(__FILE__).c_str(),__LINE__);

		printf("%s\n",szBuf);
	}


	glPushMatrix();
	glMatrixMode(GL_PROJECTION);

	glLoadIdentity();

	const Metrics &metrics= Metrics::getInstance();
	GLint view[]= {0, 0, metrics.getVirtualW(), metrics.getVirtualH()};
	//GLint view[4];
	//glGetIntegerv(GL_VIEWPORT, view);

	gluPickMatrix(x, y, w, h, view);
	gluPerspective(perspFov, metrics.getAspectRatio(), perspNearPlane, perspFarPlane);
	//gluPerspective(perspFov, metrics.getAspectRatio(), 0.0001, 1000.0);
	//gluPerspective(perspFov, (float)view[2]/(float)view[3], perspNearPlane, perspFarPlane);
	loadGameCameraMatrix();

	//render units to find which ones should be selected
	renderUnitsFast();
	if(withObjectSelection == true) {
		renderObjectsFast(false,true);
	}

	//pop matrices
	glPopMatrix();

	// Added this to ensure all the selection calls are done now
	// (see http://www.unknownroad.com/rtfm/graphics/glselection.html section: [0x4])
	//glFlush();

	//select units by checking the selected buffer
	int selCount= glRenderMode(GL_RENDER);
	if(selCount > 0) {
		VisibleQuadContainerCache &qCache = getQuadCache();
		for(int i = 1; i <= selCount; ++i) {
			int index = selectBuffer[i*4-1];
			if(index >= OBJECT_SELECT_OFFSET) {
				Object *object = qCache.visibleObjectList[index - OBJECT_SELECT_OFFSET];
				if(object != NULL) {
					obj = object;
					if(withObjectSelection == true) {
						break;
					}
				}
			}
			else {
				Unit *unit = qCache.visibleQuadUnitList[index];
				if(unit != NULL && unit->isAlive()) {
					units.push_back(unit);
				}
			}
		}
	}
	else if(selCount < 0) {
		const char *errorString= reinterpret_cast<const char*>(gluErrorString(selCount));
		char szBuf[8096]="";
		snprintf(szBuf,8096,"OpenGL error #%d [0x%X] : [%s] at file: [%s], line: %d",selCount,selCount,errorString,extractFileFromDirectoryPath(__FILE__).c_str(),__LINE__);

		printf("%s\n",szBuf);
	}
}

void Renderer::selectUsingColorPicking(Selection::UnitContainer &units,
		const Object *&obj, const bool withObjectSelection,
		const Vec2i &posDown, const Vec2i &posUp) {
	int x1 = posDown.x;
	int y1 = posDown.y;
	int x2 = posUp.x;
	int y2 = posUp.y;

	int x = min(x1,x2);
	int y = min(y1,y2);
	int w = max(x1,x2) - min(x1,x2);
	int h = max(y1,y2) - min(y1,y2);
	if(w < 2) {
		w = 2;
	}
	if(h < 2) {
		h = 2;
	}

	const Metrics &metrics= Metrics::getInstance();
	x= (x * metrics.getScreenW() / metrics.getVirtualW());
	y= (y * metrics.getScreenH() / metrics.getVirtualH());

	w= (w * metrics.getScreenW() / metrics.getVirtualW());
	h= (h * metrics.getScreenH() / metrics.getVirtualH());

	PixelBufferWrapper::begin();


	glPushMatrix();
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	//GLint view[]= {0, 0, metrics.getVirtualW(), metrics.getVirtualH()};
	//gluPickMatrix(x, y, w, h, view);
	gluPerspective(perspFov, metrics.getAspectRatio(), perspNearPlane, perspFarPlane);
	loadGameCameraMatrix();

	//render units to find which ones should be selected
	//printf("In [%s::%s] Line: %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	vector<Unit *> rendererUnits = renderUnitsFast(false, true);
	//printf("In [%s::%s] Line: %d rendererUnits = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,rendererUnits.size());


	// Added this to ensure all the selection calls are done now
	// (see http://www.unknownroad.com/rtfm/graphics/glselection.html section: [0x4])
	//glFlush();

	//GraphicsInterface::getInstance().getCurrentContext()->swapBuffers();

	PixelBufferWrapper::end();

	vector<BaseColorPickEntity *> unitsVector;
	bool unitFound=false;

	if(rendererUnits.empty() == false) {
		copy(rendererUnits.begin(), rendererUnits.end(), std::inserter(unitsVector, unitsVector.begin()));
	}

	if(unitsVector.empty() == false) {
		vector<int> pickedList = BaseColorPickEntity::getPickedList(x,y,w,h, unitsVector);
		//printf("In [%s::%s] Line: %d pickedList = %d models rendered = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,pickedList.size(),rendererModels.size());

		if(pickedList.empty() == false) {
			units.reserve(pickedList.size());
			for(unsigned int i = 0; i < pickedList.size(); ++i) {
				int index = pickedList[i];
				//printf("In [%s::%s] Line: %d searching for selected object i = %d index = %d units = %d objects = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,i,index,rendererUnits.size(),rendererObjects.size());

				if(rendererUnits.empty() == false && index < (int)rendererUnits.size()) {
					Unit *unit = rendererUnits[index];
					if(unit != NULL && unit->isAlive()) {
						unitFound=true;
						units.push_back(unit);
					}
				}
			}
		}
	}

	if(withObjectSelection == true && unitFound==false) {
		vector<Object *> rendererObjects;
		vector<BaseColorPickEntity *> objectsVector;
		rendererObjects = renderObjectsFast(false,true,true);
		if(rendererObjects.empty() == false) {
			copy(rendererObjects.begin(), rendererObjects.end(), std::inserter(objectsVector, objectsVector.begin()));
		}

		if(objectsVector.empty() == false) {
			vector<int> pickedList = BaseColorPickEntity::getPickedList(x,y,w,h, objectsVector);
			//printf("In [%s::%s] Line: %d pickedList = %d models rendered = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,pickedList.size(),rendererModels.size());

			if(pickedList.empty() == false) {
				for(unsigned int i = 0; i < pickedList.size(); ++i) {
					int index = pickedList[i];
					//printf("In [%s::%s] Line: %d searching for selected object i = %d index = %d units = %d objects = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,i,index,rendererUnits.size(),rendererObjects.size());

					if(rendererObjects.empty() == false && index < (int)rendererObjects.size()) {
						Object *object = rendererObjects[index];
						//printf("In [%s::%s] Line: %d searching for selected object i = %d index = %d [%p]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,i,index,object);

						if(object != NULL) {
							obj = object;
							break;
						}
					}
				}
			}
		}
	}
	//pop matrices
	glPopMatrix();
}

// ==================== shadows ====================

void Renderer::renderShadowsToTexture(const int renderFps){
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	if(shadowsOffDueToMinRender == false &&
		(shadows == sProjected || shadows == sShadowMapping)) {

		shadowMapFrame= (shadowMapFrame + 1) % (shadowFrameSkip + 1);

		if(shadowMapFrame == 0){
			assertGl();

			glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT | GL_POLYGON_BIT);

			if(shadows == sShadowMapping) {
				glClear(GL_DEPTH_BUFFER_BIT);
			}
			else {
				float color= 1.0f-shadowIntensity;
				glColor3f(color, color, color);
				glClearColor(1.f, 1.f, 1.f, 1.f);
				glDisable(GL_DEPTH_TEST);
				glClear(GL_COLOR_BUFFER_BIT);
			}

			//assertGl();

			//clear color buffer
			//
			//set viewport, we leave one texel always in white to avoid problems
			glViewport(1, 1, shadowTextureSize-2, shadowTextureSize-2);

			//assertGl();

			if(nearestLightPos.w == 0.f) {
				//directional light

				//light pos
				assert(game != NULL);
				assert(game->getWorld() != NULL);
				const TimeFlow *tf= game->getWorld()->getTimeFlow();
				assert(tf != NULL);
				float ang= tf->isDay()? computeSunAngle(tf->getTime()): computeMoonAngle(tf->getTime());
				ang= radToDeg(ang);

				//push and set projection
				glMatrixMode(GL_PROJECTION);
				glPushMatrix();
				glLoadIdentity();

				//assertGl();

				if(game->getGameCamera()->getState()==GameCamera::sGame){
					//glOrtho(-35, 5, -15, 15, -1000, 1000);
					//glOrtho(-30, 30, -20, 20, -1000, 1000);
					glOrtho(-30, 5, -20, 20, -1000, 1000);
				}
				else{
					glOrtho(-30, 30, -20, 20, -1000, 1000);
				}

				//assertGl();

				//push and set modelview
				glMatrixMode(GL_MODELVIEW);
				glPushMatrix();
				glLoadIdentity();

				glRotatef(15, 0, 1, 0);

				glRotatef(ang, 1, 0, 0);
				glRotatef(90, 0, 1, 0);
				const Vec3f &pos= game->getGameCamera()->getPos();

				glTranslatef(static_cast<int>(-pos.x), 0, static_cast<int>(-pos.z));

				//assertGl();
			}
			else {
				//non directional light

				//push projection
				glMatrixMode(GL_PROJECTION);
				glPushMatrix();
				glLoadIdentity();

				//assertGl();

				gluPerspective(perspFov, 1.f, perspNearPlane, perspFarPlane);
				//const Metrics &metrics= Metrics::getInstance();
				//gluPerspective(perspFov, metrics.getAspectRatio(), perspNearPlane, perspFarPlane);


				assertGl();

				//push modelview
				glMatrixMode(GL_MODELVIEW);
				glPushMatrix();
				glLoadIdentity();
				glRotatef(-90, -1, 0, 0);
				glTranslatef(-nearestLightPos.x, -nearestLightPos.y-2, -nearestLightPos.z);

				//assertGl();
			}

			if(shadows == sShadowMapping) {
				glEnable(GL_POLYGON_OFFSET_FILL);
				glPolygonOffset(1.0f, 16.0f);

				//assertGl();
			}

			//render 3d
			renderUnitsFast(true);
			renderObjectsFast(true,false);

			//assertGl();

			//read color buffer
			glBindTexture(GL_TEXTURE_2D, shadowMapHandle);
			assertGl();
			glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, shadowTextureSize, shadowTextureSize);
			GLenum error = glGetError();
			// This error can happen when a Linux user switches from an X session
			// back to a running game, and 'seems' to be safe to ignore it
			if(error != GL_INVALID_OPERATION) {
				assertGlWithErrorNumber(error);
			}

			//get elemental matrices
			static Matrix4f matrix1;
			static bool matrix1Populate = true;
			if(matrix1Populate == true) {
				matrix1Populate = false;
				matrix1[0]= 0.5f;	matrix1[4]= 0.f;	matrix1[8]= 0.f;	matrix1[12]= 0.5f;
				matrix1[1]= 0.f;	matrix1[5]= 0.5f;	matrix1[9]= 0.f;	matrix1[13]= 0.5f;
				matrix1[2]= 0.f;	matrix1[6]= 0.f;	matrix1[10]= 0.5f;	matrix1[14]= 0.5f;
				matrix1[3]= 0.f;	matrix1[7]= 0.f;	matrix1[11]= 0.f;	matrix1[15]= 1.f;
			}
			Matrix4f matrix2;
			glGetFloatv(GL_PROJECTION_MATRIX, matrix2.ptr());

			//assertGl();

			Matrix4f matrix3;
			glGetFloatv(GL_MODELVIEW_MATRIX, matrix3.ptr());

			//pop both matrices
			glPopMatrix();
			glMatrixMode(GL_PROJECTION);
			glPopMatrix();

			glMatrixMode(GL_PROJECTION);
			glPushMatrix();

			//assertGl();

			//compute texture matrix
			glLoadMatrixf(matrix1.ptr());
			glMultMatrixf(matrix2.ptr());
			glMultMatrixf(matrix3.ptr());
			glGetFloatv(GL_TRANSPOSE_PROJECTION_MATRIX_ARB, shadowMapMatrix.ptr());

			//assertGl();

			//if(shadows == sShadowMapping) {
			//	glDisable(GL_POLYGON_OFFSET_FILL);
			//	glPolygonOffset(0.0f, 0.0f);
			//}

			//pop
			glPopMatrix();

			//assertGl();

			glPopAttrib();

			assertGl();
		}
	}
}


// ==================== gl wrap ====================

string Renderer::getGlInfo(){
	string infoStr="";
	Lang &lang= Lang::getInstance();

	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == false) {
		infoStr+= lang.getString("OpenGlInfo")+":\n";
		infoStr+= "   "+lang.getString("OpenGlVersion")+": ";
		infoStr+= string((getGlVersion() != NULL ? getGlVersion() : "?"))+"\n";
		infoStr+= "   "+lang.getString("OpenGlRenderer")+": ";
		infoStr+= string((getGlVersion() != NULL ? getGlVersion() : "?"))+"\n";
		infoStr+= "   "+lang.getString("OpenGlVendor")+": ";
		infoStr+= string((getGlVendor() != NULL ? getGlVendor() : "?"))+"\n";
		infoStr+= "   "+lang.getString("OpenGlMaxLights")+": ";
		infoStr+= intToStr(getGlMaxLights())+"\n";
		infoStr+= "   "+lang.getString("OpenGlMaxTextureSize")+": ";
		infoStr+= intToStr(getGlMaxTextureSize())+"\n";
		infoStr+= "   "+lang.getString("OpenGlMaxTextureUnits")+": ";
		infoStr+= intToStr(getGlMaxTextureUnits())+"\n";
		infoStr+= "   "+lang.getString("OpenGlModelviewStack")+": ";
		infoStr+= intToStr(getGlModelviewMatrixStackDepth())+"\n";
		infoStr+= "   "+lang.getString("OpenGlProjectionStack")+": ";
		infoStr+= intToStr(getGlProjectionMatrixStackDepth())+"\n";
	}
	return infoStr;
}

string Renderer::getGlMoreInfo(){
	string infoStr="";
	Lang &lang= Lang::getInstance();

	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == false) {
		//gl extensions
		infoStr+= lang.getString("OpenGlExtensions")+":\n   ";

		string extensions= getGlExtensions();
		int charCount= 0;
		for(int i = 0; i < (int)extensions.size(); ++i) {
			infoStr+= extensions[i];
			if(charCount>120 && extensions[i]==' '){
				infoStr+= "\n   ";
				charCount= 0;
			}
			++charCount;
		}

		//platform extensions
		infoStr+= "\n\n";
		infoStr+= lang.getString("OpenGlPlatformExtensions")+":\n   ";

		charCount= 0;
		string platformExtensions= getGlPlatformExtensions();
		for(int i = 0; i < (int)platformExtensions.size(); ++i) {
			infoStr+= platformExtensions[i];
			if(charCount>120 && platformExtensions[i]==' '){
				infoStr+= "\n   ";
				charCount= 0;
			}
			++charCount;
		}
	}

	return infoStr;
}

void Renderer::autoConfig() {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == false) {
		Config &config= Config::getInstance();

		bool nvidiaCard= toLower(getGlVendor()).find("nvidia")!=string::npos;
		bool atiCard= toLower(getGlVendor()).find("ati")!=string::npos;
		//bool shadowExtensions = isGlExtensionSupported("GL_ARB_shadow") && isGlExtensionSupported("GL_ARB_shadow_ambient");
		bool shadowExtensions = isGlExtensionSupported("GL_ARB_shadow");

		//3D textures
		config.setBool("Textures3D", isGlExtensionSupported("GL_EXT_texture3D"));

		//shadows
		string shadows="";
		if(getGlMaxTextureUnits()>=3){
			if(nvidiaCard && shadowExtensions){
				shadows= shadowsToStr(sShadowMapping);
			}
			else{
				shadows= shadowsToStr(sProjected);
			}
		}
		else{
			shadows=shadowsToStr(sDisabled);
		}
		config.setString("Shadows", shadows);

		//lights
		config.setInt("MaxLights", atiCard? 1: 4);

		//filter
		config.setString("Filter", "Bilinear");
	}
}

void Renderer::clearBuffers() {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}

void Renderer::clearZBuffer() {
	glClear(GL_DEPTH_BUFFER_BIT);
}

void Renderer::loadConfig() {
	Config &config= Config::getInstance();

	//cache most used config params
	maxLights= config.getInt("MaxLights");
	photoMode= config.getBool("PhotoMode");
	focusArrows= config.getBool("FocusArrows");
	textures3D= config.getBool("Textures3D");
	float gammaValue=config.getFloat("GammaValue","0.0");
	if(this->program == NULL) {
		throw megaglest_runtime_error("this->program == NULL");
	}
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == false) {
		//if(this->program != NULL) {
		if(gammaValue != 0.0) {
			this->program->getWindow()->setGamma(gammaValue);
		}
		//}
	}
	//load shadows
	shadows= strToShadows(config.getString("Shadows"));
	if(shadows==sProjected || shadows==sShadowMapping){
		shadowTextureSize= config.getInt("ShadowTextureSize");
		shadowFrameSkip= config.getInt("ShadowFrameSkip");
		shadowIntensity= config.getFloat("ShadowIntensity","1.0");
	}

	//load filter settings
	Texture2D::Filter textureFilter= strToTextureFilter(config.getString("Filter"));
	int maxAnisotropy= config.getInt("FilterMaxAnisotropy");

	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == false) {
		for(int i=0; i<rsCount; ++i){
			textureManager[i]->setFilter(textureFilter);
			textureManager[i]->setMaxAnisotropy(maxAnisotropy);
		}
	}
}

Texture2D *Renderer::saveScreenToTexture(int x, int y, int width, int height) {
	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	Config &config= Config::getInstance();
	Texture2D::Filter textureFilter = strToTextureFilter(config.getString("Filter"));
	int maxAnisotropy				= config.getInt("FilterMaxAnisotropy");

	Texture2D *texture = GraphicsInterface::getInstance().getFactory()->newTexture2D();
	texture->setForceCompressionDisabled(true);
	texture->setMipmap(false);
	Pixmap2D *pixmapScreenShot = texture->getPixmap();
	pixmapScreenShot->init(width, height, 3);
	texture->init(textureFilter,maxAnisotropy);

	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
	//glFinish();

	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
	glReadPixels(x, y, pixmapScreenShot->getW(), pixmapScreenShot->getH(),
				 GL_RGB, GL_UNSIGNED_BYTE, pixmapScreenShot->getPixels());

	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	return texture;
}

void Renderer::saveScreen(const string &path,int w, int h) {
	const Metrics &sm= Metrics::getInstance();

	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	Pixmap2D *pixmapScreenShot = new Pixmap2D(sm.getScreenW(),sm.getScreenH(), 3);

	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
	//glFinish();

	glPixelStorei(GL_PACK_ALIGNMENT, 1);

	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
	glReadPixels(0, 0, pixmapScreenShot->getW(), pixmapScreenShot->getH(),
				 GL_RGB, GL_UNSIGNED_BYTE, pixmapScreenShot->getPixels());

	if(w==0 || h==0){
		h=sm.getScreenH();
		w=sm.getScreenW();
	}
	else{
		pixmapScreenShot->Scale(GL_RGB,w,h);
	}
	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	// Signal the threads queue to add a screenshot save request
	MutexSafeWrapper safeMutex(saveScreenShotThreadAccessor,string(extractFileFromDirectoryPath(__FILE__).c_str()) + "_" + intToStr(__LINE__));
	saveScreenQueue.push_back(make_pair(path,pixmapScreenShot));
	safeMutex.ReleaseLock();

	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
}

unsigned int Renderer::getSaveScreenQueueSize() {
	MutexSafeWrapper safeMutex(saveScreenShotThreadAccessor,string(extractFileFromDirectoryPath(__FILE__).c_str()) + "_" + intToStr(__LINE__));
	int queueSize = (int)saveScreenQueue.size();
	safeMutex.ReleaseLock();

	return queueSize;
}

// ==================== PRIVATE ====================

float Renderer::computeSunAngle(float time) {

	float dayTime= TimeFlow::dusk-TimeFlow::dawn;
	float fTime= (time-TimeFlow::dawn)/dayTime;
	return clamp(fTime*pi, pi/8.f, 7.f*pi/8.f);
}

float Renderer::computeMoonAngle(float time) {
	float nightTime= 24-(TimeFlow::dusk-TimeFlow::dawn);

	if(time<TimeFlow::dawn){
		time+= 24.f;
	}

	float fTime= (time-TimeFlow::dusk)/nightTime;
	return clamp((1.0f-fTime)*pi, pi/8.f, 7.f*pi/8.f);
}

Vec4f Renderer::computeSunPos(float time) {
	float ang= computeSunAngle(time);
	return Vec4f(-std::cos(ang)*sunDist, std::sin(ang)*sunDist, 0.f, 0.f);
}

Vec4f Renderer::computeMoonPos(float time) {
	float ang= computeMoonAngle(time);
	return Vec4f(-std::cos(ang)*moonDist, std::sin(ang)*moonDist, 0.f, 0.f);
}

// ==================== fast render ====================

//render units for selection purposes
vector<Unit *> Renderer::renderUnitsFast(bool renderingShadows, bool colorPickingSelection) {
	vector<Unit *> unitsList;
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return unitsList;
	}

	assert(game != NULL);
	//const World *world= game->getWorld();
	//assert(world != NULL);

	VisibleQuadContainerCache &qCache = getQuadCache();
	if(qCache.visibleQuadUnitList.empty() == false) {
		if(colorPickingSelection == true) {
			unitsList.reserve(qCache.visibleQuadUnitList.size());
		}

		bool modelRenderStarted = false;
		bool renderOnlyBuildings=true;
		for(int k=0; k<2 ;k++) {
			if(k==0){
				//glDisable(GL_DEPTH_TEST);
				renderOnlyBuildings=true;
			}
			else {
				if(colorPickingSelection == true){
					// clear depth buffer to get units behind buildings rendered in front of them
					glClear(GL_DEPTH_BUFFER_BIT);
				}
				//glEnable(GL_DEPTH_TEST);
				renderOnlyBuildings=false;
			}
			for(int visibleUnitIndex = 0;
					visibleUnitIndex < (int)qCache.visibleQuadUnitList.size(); ++visibleUnitIndex) {
				Unit *unit = qCache.visibleQuadUnitList[visibleUnitIndex];

				if(renderingShadows==false && unit->isAlive()==false){
					// no need to render dead units for selection
					continue;
				}

				if(renderOnlyBuildings==true && unit->getType()->hasSkillClass(scMove)){
					continue;
				}

				if(renderOnlyBuildings==false && !unit->getType()->hasSkillClass(scMove)){
					continue;
				}

				if(modelRenderStarted == false) {
					modelRenderStarted = true;

					if(colorPickingSelection == false) {
						//glPushAttrib(GL_ENABLE_BIT| GL_TEXTURE_BIT);
						glDisable(GL_LIGHTING);
						if (renderingShadows == false) {
							glPushAttrib(GL_ENABLE_BIT);
							glDisable(GL_TEXTURE_2D);
						}
						else {
							glPushAttrib(GL_ENABLE_BIT| GL_TEXTURE_BIT);
							glEnable(GL_TEXTURE_2D);
							glAlphaFunc(GL_GREATER, 0.4f);

							glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

							//set color to the texture alpha
							glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
							glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR);
							glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);

							//set alpha to the texture alpha
							glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
							glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
							glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
						}
					}

					modelRenderer->begin(false, renderingShadows, false, colorPickingSelection);

					if(colorPickingSelection == false) {
						glInitNames();
					}
				}

				if(colorPickingSelection == false) {
					glPushName(visibleUnitIndex);
				}

				//assertGl();

				glMatrixMode(GL_MODELVIEW);
				//debuxar modelo
				glPushMatrix();

				//translate
				Vec3f currVec= unit->getCurrVectorFlat();
				glTranslatef(currVec.x, currVec.y, currVec.z);

				//rotate
				glRotatef(unit->getRotation(), 0.f, 1.f, 0.f);

				//render
				Model *model= unit->getCurrentModelPtr();
				//if(this->gameCamera->getPos().dist(unit->getCurrVector()) <= SKIP_INTERPOLATION_DISTANCE) {

					// ***MV don't think this is needed below 2013/01/11
					model->updateInterpolationVertices(unit->getAnimProgressAsFloat(), unit->isAlive() && !unit->isAnimProgressBound());

				//}

				if(colorPickingSelection == true) {
					unit->setUniquePickingColor();
					unitsList.push_back(unit);
				}

				modelRenderer->render(model,rmSelection);

				glPopMatrix();

				if(colorPickingSelection == false) {
					glPopName();
				}
			}
		}

		if(modelRenderStarted == true) {
			modelRenderer->end();
			if(colorPickingSelection == false) {
				glPopAttrib();
			}
		}
	}
	//glDisable(GL_DEPTH_TEST);
	return unitsList;
}

//render objects for selection purposes
vector<Object *>  Renderer::renderObjectsFast(bool renderingShadows, bool resourceOnly,
		bool colorPickingSelection) {
	vector<Object *> objectList;
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return objectList;
	}

	VisibleQuadContainerCache &qCache = getQuadCache();
	if(qCache.visibleObjectList.empty() == false) {
		if(colorPickingSelection == true) {
			objectList.reserve(qCache.visibleObjectList.size());
		}

		bool modelRenderStarted = false;
		for(int visibleIndex = 0;
				visibleIndex < (int)qCache.visibleObjectList.size(); ++visibleIndex) {
			Object *o = qCache.visibleObjectList[visibleIndex];

			if(modelRenderStarted == false) {
				modelRenderStarted = true;

				if(colorPickingSelection == false) {
					glDisable(GL_LIGHTING);

					if (renderingShadows == false){
						glPushAttrib(GL_ENABLE_BIT);
						glDisable(GL_TEXTURE_2D);
					}
					else {
						glPushAttrib(GL_ENABLE_BIT| GL_TEXTURE_BIT);
						glAlphaFunc(GL_GREATER, 0.5f);

						glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

						//set color to the texture alpha
						glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
						glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR);
						glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);

						//set alpha to the texture alpha
						glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
						glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
						glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
					}
				}

				modelRenderer->begin(false, renderingShadows, false, colorPickingSelection);

				if(colorPickingSelection == false) {
					glInitNames();
				}
			}

			if(resourceOnly == false || o->getResource()!= NULL) {
				Model *objModel= o->getModelPtr();
				//if(this->gameCamera->getPos().dist(o->getPos()) <= SKIP_INTERPOLATION_DISTANCE) {

					// ***MV don't think this is needed below 2013/01/11
					//objModel->updateInterpolationData(o->getAnimProgress(), true);

				//}
				const Vec3f v= o->getConstPos();

				if(colorPickingSelection == false) {
					glPushName(OBJECT_SELECT_OFFSET+visibleIndex);
				}

				glMatrixMode(GL_MODELVIEW);
				glPushMatrix();
				glTranslatef(v.x, v.y, v.z);
				glRotatef(o->getRotation(), 0.f, 1.f, 0.f);

				if(colorPickingSelection == true) {
					o->setUniquePickingColor();
					objectList.push_back(o);
				}

				modelRenderer->render(objModel,resourceOnly?rmSelection:rmNormal);

				glPopMatrix();

				if(colorPickingSelection == false) {
					glPopName();
				}
			}
		}

		if(modelRenderStarted == true) {
			modelRenderer->end();

			if(colorPickingSelection == false) {
				glPopAttrib();
			}
		}
	}

	return objectList;
}

// ==================== gl caps ====================

void Renderer::checkGlCaps() {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	if(glActiveTexture == NULL) {
		string message;

		message += "Your system supports OpenGL version [";
 		message += getGlVersion() + string("]\n");
 		message += "MegaGlest needs a version that supports\n";
 		message += "glActiveTexture (OpenGL 1.3) or the ARB_multitexture extension.";

 		throw megaglest_runtime_error(message.c_str(),true);
	}

	//opengl 1.3
	//if(!isGlVersionSupported(1, 3, 0)) {
	if(glewIsSupported("GL_VERSION_1_3") == false) {
		string message;

		message += "Your system supports OpenGL version [";
 		message += getGlVersion() + string("]\n");
 		message += "MegaGlest needs at least version 1.3 to work\n";
 		message += "You may solve this problem by installing your latest video card drivers";

 		throw megaglest_runtime_error(message.c_str(),true);
	}

	//opengl 1.4 or extension
	//if(!isGlVersionSupported(1, 4, 0)){
	if(glewIsSupported("GL_VERSION_1_4") == false) {
		checkExtension("GL_ARB_texture_env_crossbar", "MegaGlest");
	}
}

void Renderer::checkGlOptionalCaps() {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
	//shadows
	if(shadows == sProjected || shadows == sShadowMapping) {
		if(getGlMaxTextureUnits() < 3) {
			throw megaglest_runtime_error("Your system doesn't support 3 texture units, required for shadows");
		}
	}

	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
	//shadow mapping
	if(shadows == sShadowMapping) {
		checkExtension("GL_ARB_shadow", "Shadow Mapping");
		//checkExtension("GL_ARB_shadow_ambient", "Shadow Mapping");
		//checkExtension("GL_ARB_depth_texture", "Shadow Mapping");
	}

	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
}

void Renderer::checkExtension(const string &extension, const string &msg) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
	if(!isGlExtensionSupported(extension.c_str())) {
		string str= "OpenGL extension not supported: " + extension +  ", required for " + msg;
		throw megaglest_runtime_error(str);
	}
	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
}

// ==================== init 3d lists ====================

void Renderer::init3dList() {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	render3dSetup();

	if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	//const Metrics &metrics= Metrics::getInstance();

    //assertGl();

    //if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	//list3d= glGenLists(1);
	//assertGl();
	//list3dValid=true;

	//if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	//glNewList(list3d, GL_COMPILE_AND_EXECUTE);
	//need to execute, because if not gluPerspective takes no effect and gluLoadMatrix is wrong
	//render3dSetup();
	//glEndList();

	//if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	//assert
	//assertGl();
}

void Renderer::render3dSetup() {
	//if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
	const Metrics &metrics= Metrics::getInstance();
	//if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	//misc
	glViewport(0, 0, metrics.getScreenW(), metrics.getScreenH());
	glClearColor(fowColor.x, fowColor.y, fowColor.z, fowColor.w);
	glFrontFace(GL_CW);
	glEnable(GL_CULL_FACE);
	loadProjectionMatrix();

	//if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	//if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	//texture state
	glActiveTexture(shadowTexUnit);
	glDisable(GL_TEXTURE_2D);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	glActiveTexture(fowTexUnit);
	glDisable(GL_TEXTURE_2D);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	glActiveTexture(baseTexUnit);
	glEnable(GL_TEXTURE_2D);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	//if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	//if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	//material state
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, defSpecularColor.ptr());
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, defAmbientColor.ptr());
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, defDiffuseColor.ptr());
	glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
	glColor4fv(defColor.ptr());

	//blend state
	glDisable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	//if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	//alpha test state
	glEnable(GL_ALPHA_TEST);
	glAlphaFunc(GL_GREATER, 0.f);

	//depth test state
	glEnable(GL_DEPTH_TEST);
	glDepthMask(GL_TRUE);
	glDepthFunc(GL_LESS);

	//if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	//lighting state
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);

	//matrix mode
	glMatrixMode(GL_MODELVIEW);

	//stencil test
	glDisable(GL_STENCIL_TEST);

	//if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
	//if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	//fog
	const Tileset *tileset= NULL;
	if(game != NULL && game->getWorld() != NULL) {
		//if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
		tileset = game->getWorld()->getTileset();
	}

	//if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	if(tileset != NULL && tileset->getFog()) {
		//if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

		glEnable(GL_FOG);
		if(tileset->getFogMode()==fmExp) {
			glFogi(GL_FOG_MODE, GL_EXP);
		}
		else {
			glFogi(GL_FOG_MODE, GL_EXP2);
		}

		glFogf(GL_FOG_DENSITY, tileset->getFogDensity());
		glFogfv(GL_FOG_COLOR, tileset->getFogColor().ptr());
	}

	//if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
}

void Renderer::init2dList() {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

//	//this list sets the state for the 2d rendering
//	list2d= glGenLists(1);
//	assertGl();
//	list2dValid=true;
//
//	glNewList(list2d, GL_COMPILE);
//	render2dMenuSetup();
//	glEndList();
//
//	assertGl();
}

void Renderer::render2dMenuSetup() {
	//if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	const Metrics &metrics= Metrics::getInstance();
	//projection
	glViewport(0, 0, metrics.getScreenW(), metrics.getScreenH());
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, metrics.getVirtualW(), 0, metrics.getVirtualH(), 0, 1);

	//if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	//modelview
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	//if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	//disable everything
	glDisable(GL_BLEND);
	glDisable(GL_LIGHTING);
	glDisable(GL_ALPHA_TEST);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_STENCIL_TEST);
	glDisable(GL_FOG);
	glDisable(GL_CULL_FACE);
	glFrontFace(GL_CCW);

	//if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	if(glActiveTexture != NULL) {
		glActiveTexture(baseTexUnit);
	}
	else {
		char szBuf[8096]="";
		snprintf(szBuf,8096,"In [%s::%s] Line: %d\nglActiveTexture == NULL\nglActiveTexture is only supported if the GL version is 1.3 or greater,\nor if the ARB_multitexture extension is supported!",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
		throw megaglest_runtime_error(szBuf);
	}

	//if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	glDisable(GL_TEXTURE_2D);

	//if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	//blend func
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	//color
	glColor4f(1.f, 1.f, 1.f, 1.f);

	//if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
}

void Renderer::init3dListMenu(const MainMenu *mm) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	//this->mm3d = mm;
	//printf("In [%s::%s Line: %d] this->custom_mm3d [%p] this->mm3d [%p]\n",__FILE__,__FUNCTION__,__LINE__,this->custom_mm3d,this->mm3d);

/*
	assertGl();

    if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	const Metrics &metrics= Metrics::getInstance();
	//const MenuBackground *mb= mm->getConstMenuBackground();
	const MenuBackground *mb = NULL;
	if(mm != NULL) {
		mb = mm->getConstMenuBackground();
	}

	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	if(this->customlist3dMenu != NULL) {
		*this->customlist3dMenu = glGenLists(1);
		assertGl();
	}
	else {
		list3dMenu= glGenLists(1);
		assertGl();
		list3dMenuValid=true;
	}

	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	if(this->customlist3dMenu != NULL) {
		glNewList(*this->customlist3dMenu, GL_COMPILE);
	}
	else {
		glNewList(list3dMenu, GL_COMPILE);
	}

		if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
		//misc
		glViewport(0, 0, metrics.getScreenW(), metrics.getScreenH());
		glClearColor(0.4f, 0.4f, 0.4f, 1.f);
		glFrontFace(GL_CW);
		glEnable(GL_CULL_FACE);
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(perspFov, metrics.getAspectRatio(), perspNearPlane, 1000000);

		//texture state
		glEnable(GL_TEXTURE_2D);
		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

		//material state
		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, defSpecularColor.ptr());
		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, defAmbientColor.ptr());
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, defDiffuseColor.ptr());
		glColor4fv(defColor.ptr());
		glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);

		//blend state
		glDisable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

		//alpha test state
		glEnable(GL_ALPHA_TEST);
		glAlphaFunc(GL_GREATER, 0.f);

		//depth test state
		glEnable(GL_DEPTH_TEST);
		glDepthMask(GL_TRUE);
		glDepthFunc(GL_LESS);

		//lighting state
		glEnable(GL_LIGHTING);

		//matrix mode
		glMatrixMode(GL_MODELVIEW);

		//stencil test
		glDisable(GL_STENCIL_TEST);

		if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

		//fog
		if(mb != NULL && mb->getFog()){
			if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

			glEnable(GL_FOG);
			glFogi(GL_FOG_MODE, GL_EXP2);
			glFogf(GL_FOG_DENSITY, mb->getFogDensity());
		}

		if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	glEndList();

	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	//assert
	assertGl();
*/
}

void Renderer::render3dMenuSetup(const MainMenu *mm) {
	//if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	const Metrics &metrics= Metrics::getInstance();
	const MenuBackground *mb = NULL;
	if(mm != NULL) {
		mb = mm->getConstMenuBackground();
	}

	//if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
	//misc
	glViewport(0, 0, metrics.getScreenW(), metrics.getScreenH());
	glClearColor(0.4f, 0.4f, 0.4f, 1.f);
	glFrontFace(GL_CW);
	glEnable(GL_CULL_FACE);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(perspFov, metrics.getAspectRatio(), perspNearPlane, 1000000);

	//texture state
	glEnable(GL_TEXTURE_2D);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	//material state
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, defSpecularColor.ptr());
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, defAmbientColor.ptr());
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, defDiffuseColor.ptr());
	glColor4fv(defColor.ptr());
	glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);

	//blend state
	glDisable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	//alpha test state
	glEnable(GL_ALPHA_TEST);
	glAlphaFunc(GL_GREATER, 0.f);

	//depth test state
	glEnable(GL_DEPTH_TEST);
	glDepthMask(GL_TRUE);
	glDepthFunc(GL_LESS);

	//lighting state
	glEnable(GL_LIGHTING);

	//matrix mode
	glMatrixMode(GL_MODELVIEW);

	//stencil test
	glDisable(GL_STENCIL_TEST);

	//if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	//fog
	if(mb != NULL && mb->getFog()) {
		//if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

		glEnable(GL_FOG);
		glFogi(GL_FOG_MODE, GL_EXP2);
		glFogf(GL_FOG_DENSITY, mb->getFogDensity());
	}

	//assert
	assertGl();

	//if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
}
// ==================== misc ====================

void Renderer::loadProjectionMatrix() {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	GLdouble clipping;
	const Metrics &metrics= Metrics::getInstance();

    assertGl();

	clipping= photoMode ? perspFarPlane*100 : perspFarPlane;

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(perspFov, metrics.getAspectRatio(), perspNearPlane, clipping);

    assertGl();
}

void Renderer::enableProjectiveTexturing() {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	glTexGenfv(GL_S, GL_EYE_PLANE, &shadowMapMatrix[0]);
	glTexGenfv(GL_T, GL_EYE_PLANE, &shadowMapMatrix[4]);
	glTexGenfv(GL_R, GL_EYE_PLANE, &shadowMapMatrix[8]);
	glTexGenfv(GL_Q, GL_EYE_PLANE, &shadowMapMatrix[12]);
	glEnable(GL_TEXTURE_GEN_S);
	glEnable(GL_TEXTURE_GEN_T);
	glEnable(GL_TEXTURE_GEN_R);
	glEnable(GL_TEXTURE_GEN_Q);
}

// ==================== private aux drawing ====================
void Renderer::renderHealthBar(Vec3f v, Unit *unit, float height, bool lineBorder, const Texture2D *texture, const Texture2D *backgroundTexture) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	int numberOfBars=1;
	int barCount=0;
	float hp=unit->getHpRatio();
	float ep=-1.f;
	if(unit->getType()->getTotalMaxEp(unit->getTotalUpgrade()) !=0 ) {
			ep=unit->getEpRatio();
			numberOfBars++;
	}
	int productionPercent=unit->getProductionPercent();
	if(productionPercent!=-1) {
		numberOfBars++;
	}
	int size=unit->getType()->getSize();


	Vec3f rightVector;
	Vec3f upVector;
	Vec3f rightVectorTexture;
	Vec3f upVectorTexture;
	v.y+=1;
	float modelview[16];
	float width=(float)size/6+0.25f;
	float red;
	float green;
	float brightness=0.8f;

	glMatrixMode(GL_MODELVIEW);
    glPushMatrix();

	// get the current modelview state
	glGetFloatv(GL_MODELVIEW_MATRIX , modelview);
	rightVector= Vec3f(modelview[0], modelview[4], modelview[8]);
	upVector= Vec3f(modelview[1], modelview[5], modelview[9]);
	rightVectorTexture=rightVector*2;
	upVectorTexture=upVector*4;

	//from green to yellow to red

	if(hp >= 0.75f) {
		green=1;
		red=1-((2*hp-1)-0.5f);
	} else {
		red=1;
		green=0.5f+(2*hp-1);
	}

	if(red>1.0f) red=1.0f;
	if(green>1.0f) green=1.0f;
	float yOffset=(float)numberOfBars/2.f;

	if(backgroundTexture!=NULL) {
		//backgroundTexture
		glEnable(GL_TEXTURE_2D);
		glBindTexture(GL_TEXTURE_2D, static_cast<const Texture2DGl*>(backgroundTexture)->getHandle());
		glColor4f(1.f,1.f,1.f,1.f);
		//glColor4f(red+0.1f,green+0.1f,0.1f,0.5f);
		glBegin(GL_QUADS);
			glTexCoord2i(0,1);
			glVertex3fv((v - (rightVectorTexture*width - upVectorTexture*height*yOffset)).ptr());
			glTexCoord2i(0,0);
			glVertex3fv((v - (rightVectorTexture*width + upVectorTexture*height*yOffset)).ptr());
			glTexCoord2i(1,0);
			glVertex3fv((v + (rightVectorTexture*width - upVectorTexture*height*yOffset)).ptr());
			glTexCoord2i(1,1);
			glVertex3fv((v + (rightVectorTexture*width + upVectorTexture*height*yOffset)).ptr());
		glEnd();
		glDisable(GL_TEXTURE_2D);
	}

	//healthbar
	glColor4f(red*brightness,green*brightness,0.0f,0.4f);
	//hpbar
	barCount++;
	internalRenderHp(numberOfBars,barCount,hp,v,width,height,rightVector,upVector);


	if(ep > -1.0f) {
		//epbar
		barCount++;
		//glColor4f(brightness,0,brightness,0.5f);
		glColor4f(.15f*brightness,0.3f*brightness,0.8f*brightness,0.7f);
		internalRenderHp(numberOfBars,barCount,ep,v,width,height,rightVector,upVector);
	}
	if(productionPercent!=-1) {
		barCount++;
		glColor4f(brightness,0,brightness,0.6f);
		//glColor4f(0.0f*brightness,0.4f*brightness,0.2f*brightness,0.8f);
		internalRenderHp(numberOfBars,barCount,(float)productionPercent/100,v,width,height,rightVector,upVector);
	}


//	glBegin(GL_QUADS);
//		if(ep < -2.0f) {
//			//hpbar
//			glVertex3fv((v - (rightVector*width - upVector*height)).ptr());
//			glVertex3fv((v - (rightVector*width + upVector*height)).ptr());
//			glVertex3fv((v + (rightVector*hp*width - upVector*height)).ptr());
//			glVertex3fv((v + (rightVector*hp*width + upVector*height)).ptr());
//
//		} else {
//			//hpbar
//			glVertex3fv((v - (rightVector*width - upVector*height)).ptr());
//			glVertex3fv((v - (rightVector*width + upVector*height*0.0f)).ptr());
//			glVertex3fv((v + (rightVector*hp*width - upVector*height*0.0f)).ptr());
//			glVertex3fv((v + (rightVector*hp*width + upVector*height)).ptr());
//			//epbar
//			glColor4f(brightness,0,brightness,0.4f);
//			glVertex3fv((v - (rightVector*width + upVector*height*0.0f)).ptr());
//			glVertex3fv((v - (rightVector*width + upVector*height)).ptr());
//			glVertex3fv((v + (rightVector*ep*width - upVector*height)).ptr());
//			glVertex3fv((v + (rightVector*ep*width - upVector*height*0.0f)).ptr());
//		}
//	glEnd();

	if(lineBorder) {
		//border
		glColor4f(red*brightness,green*brightness,0.1f*brightness,0.5f);
		glBegin(GL_LINE_LOOP);
			glVertex3fv((v - (rightVector*width - upVector*height*yOffset)).ptr());
			glVertex3fv((v - (rightVector*width + upVector*height*yOffset)).ptr());
			glVertex3fv((v + (rightVector*width - upVector*height*yOffset)).ptr());
			glVertex3fv((v + (rightVector*width + upVector*height*yOffset)).ptr());
		glEnd();
	}

	if(texture!=NULL) {
		//BorderTexture
		glEnable(GL_TEXTURE_2D);
		glBindTexture(GL_TEXTURE_2D, static_cast<const Texture2DGl*>(texture)->getHandle());
		glColor4f(1.f,1.f,1.f,1.f);
		//glColor4f(red+0.1f,green+0.1f,0.1f,0.5f);
		glBegin(GL_QUADS);
			glTexCoord2i(0,1);
			glVertex3fv((v - (rightVectorTexture*width - upVectorTexture*height*yOffset)).ptr());
			glTexCoord2i(0,0);
			glVertex3fv((v - (rightVectorTexture*width + upVectorTexture*height*yOffset)).ptr());
			glTexCoord2i(1,0);
			glVertex3fv((v + (rightVectorTexture*width - upVectorTexture*height*yOffset)).ptr());
			glTexCoord2i(1,1);
			glVertex3fv((v + (rightVectorTexture*width + upVectorTexture*height*yOffset)).ptr());
		glEnd();
		glDisable(GL_TEXTURE_2D);
	}

    glPopMatrix();
}

void Renderer::internalRenderHp(int numberOfBars, int barNumber, float hp,
		Vec3f posVector, float width, float singleHPheight, Vec3f rightVector, Vec3f upVector) {

	float yOffset=(float)numberOfBars*singleHPheight/2;
	float offsetTop=yOffset-singleHPheight*(barNumber-1);
	float offsetBottom=yOffset-singleHPheight*barNumber;
	offsetBottom=offsetBottom*-1;
	hp=hp*2-1;

	glBegin(GL_QUADS);
		glVertex3fv((posVector - (rightVector*width - upVector*offsetTop)).ptr());
		glVertex3fv((posVector - (rightVector*width + upVector*offsetBottom)).ptr());
		glVertex3fv((posVector + (rightVector*hp*width - upVector*offsetBottom)).ptr());
		glVertex3fv((posVector + (rightVector*hp*width + upVector*offsetTop)).ptr());
	glEnd();
}

void Renderer::renderSelectionCircle(Vec3f v, int size, float radius, float thickness) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	GLUquadricObj *disc;

	glMatrixMode(GL_MODELVIEW);
    glPushMatrix();

	glTranslatef(v.x, v.y, v.z);
	glRotatef(90.f, 1.f, 0.f, 0.f);
	disc= gluNewQuadric();
	gluQuadricDrawStyle(disc, GLU_FILL);
	gluCylinder(disc, radius*(size-thickness), radius*size, thickness, 30, 1);
	gluDeleteQuadric(disc);

    glPopMatrix();
    //	glBegin (GL_QUAD_STRIP);
    //	for (float k = 0; k <= 180; k=k+1) {
    //		float j=degToRad(k);
    //		glVertex3f(v.x+std::cos(j)*.9*radius*size, v.y+thickness, v.z+std::sin(j)*.9*radius*size);
    //		glVertex3f(v.x+std::cos(j)*radius*size, v.y, v.z+std::sin(j)*radius*size);
    //	}
    //	glEnd();
}

void Renderer::renderArrow(const Vec3f &pos1, const Vec3f &pos2,
						   const Vec3f &color, float width) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	Config &config= Config::getInstance();
	if(config.getBool("RecordMode","false") == true) {
		return;
	}

	const int tesselation= 3;
	const float arrowEndSize= 0.4f;
	const float maxlen= 25;
	const float blendDelay= 5.f;

	Vec3f dir= Vec3f(pos2-pos1);
	float len= dir.length();

	if(len>maxlen) {
		return;
	}
	float alphaFactor= clamp((maxlen-len)/blendDelay, 0.f, 1.f);

	dir.normalize();
	Vec3f normal= dir.cross(Vec3f(0, 1, 0));

	Vec3f pos2Left= pos2 + normal*(width-0.05f) - dir*arrowEndSize*width;
	Vec3f pos2Right= pos2 - normal*(width-0.05f) - dir*arrowEndSize*width;
	Vec3f pos1Left= pos1 + normal*(width+0.05f);
	Vec3f pos1Right= pos1 - normal*(width+0.05f);

	//arrow body
	glBegin(GL_TRIANGLE_STRIP);
	for(int i=0; i<=tesselation; ++i) {
		float t= static_cast<float>(i)/tesselation;
		Vec3f a= pos1Left.lerp(t, pos2Left);
		Vec3f b= pos1Right.lerp(t, pos2Right);
		Vec4f c= Vec4f(color, t*0.25f*alphaFactor);

		glColor4fv(c.ptr());

		glVertex3fv(a.ptr());
		glVertex3fv(b.ptr());

	}

	glEnd();
	//arrow end
	glBegin(GL_TRIANGLES);
		glVertex3fv((pos2Left + normal*(arrowEndSize-0.1f)).ptr());
		glVertex3fv((pos2Right - normal*(arrowEndSize-0.1f)).ptr());
		glVertex3fv((pos2 + dir*(arrowEndSize-0.1f)).ptr());
	glEnd();
}

void Renderer::renderProgressBar3D(int size, int x, int y, Font3D *font, int customWidth,
		string prefixLabel,bool centeredText,int customHeight) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	// Makiong this smaller than 14 is a bad idea (since the font size is never smaller than that)
	int progressbarHeight	= (customHeight > 0 ? customHeight : 14);
    int currentSize     	= size;
    int maxSize         	= maxProgressBar;
    string renderText   	= intToStr(static_cast<int>(size)) + "%";
    if(customWidth > 0) {
        if(size > 0) {
            currentSize     = (int)((double)customWidth * ((double)size / 100.0));
        }
        maxSize         = customWidth;
        //if(maxSize <= 0) {
        //	maxSize = maxProgressBar;
        //}
    }
    if(prefixLabel != "") {
        renderText = prefixLabel + renderText;
    }

	//bar
	glBegin(GL_QUADS);
		glColor4fv(progressBarFront2.ptr());
		glVertex2i(x, y);
		glVertex2i(x, y + progressbarHeight);
		glColor4fv(progressBarFront1.ptr());
		glVertex2i(x + currentSize, y + progressbarHeight);
		glVertex2i(x + currentSize, y);
	glEnd();

	//transp bar
	glEnable(GL_BLEND);
	glBegin(GL_QUADS);
		glColor4fv(progressBarBack2.ptr());
		glVertex2i(x + currentSize, y);
		glVertex2i(x + currentSize, y + progressbarHeight);
		glColor4fv(progressBarBack1.ptr());
		glVertex2i(x + maxSize, y + progressbarHeight);
		glVertex2i(x + maxSize, y);
	glEnd();
	glDisable(GL_BLEND);

	//text
	//glColor3fv(defColor.ptr());
	//printf("Render progress bar3d renderText [%s] y = %d, centeredText = %d\n",renderText.c_str(),y, centeredText);

	renderTextBoundingBox3D(renderText, font, defColor, x, y, maxSize,
			progressbarHeight, true, true, false,-1,-1);
}

void Renderer::renderProgressBar(int size, int x, int y, Font2D *font, int customWidth,
		string prefixLabel,bool centeredText) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

    int currentSize     = size;
    int maxSize         = maxProgressBar;
    string renderText   = intToStr(static_cast<int>(size)) + "%";
    if(customWidth > 0) {
        if(size > 0) {
            currentSize     = (int)((double)customWidth * ((double)size / 100.0));
        }
        maxSize         = customWidth;
        //if(maxSize <= 0) {
        //	maxSize = maxProgressBar;
        //}
    }
    if(prefixLabel != "") {
        renderText = prefixLabel + renderText;
    }

	//bar
	glBegin(GL_QUADS);
		glColor4fv(progressBarFront2.ptr());
		glVertex2i(x, y);
		glVertex2i(x, y+10);
		glColor4fv(progressBarFront1.ptr());
		glVertex2i(x + currentSize, y+10);
		glVertex2i(x + currentSize, y);
	glEnd();

	//transp bar
	glEnable(GL_BLEND);
	glBegin(GL_QUADS);
		glColor4fv(progressBarBack2.ptr());
		glVertex2i(x + currentSize, y);
		glVertex2i(x + currentSize, y+10);
		glColor4fv(progressBarBack1.ptr());
		glVertex2i(x + maxSize, y+10);
		glVertex2i(x + maxSize, y);
	glEnd();
	glDisable(GL_BLEND);

	//text
	glColor3fv(defColor.ptr());

	//textRenderer->begin(font);
	TextRendererSafeWrapper safeTextRender(textRenderer,font);
	if(centeredText == true) {
		textRenderer->render(renderText.c_str(), x + maxSize / 2, y, centeredText);
	}
	else {
		textRenderer->render(renderText.c_str(), x, y, centeredText);
	}
	//textRenderer->end();
	safeTextRender.end();
}


void Renderer::renderTile(const Vec2i &pos) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	const Map *map= game->getWorld()->getMap();
	Vec2i scaledPos= pos * Map::cellScale;

	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glTranslatef(-0.5f, 0.f, -0.5f);

	glInitNames();
	for(int i=0; i < Map::cellScale; ++i) {
		for(int j=0; j < Map::cellScale; ++j) {

			Vec2i renderPos= scaledPos + Vec2i(i, j);

			glPushName(renderPos.y);
			glPushName(renderPos.x);

			glDisable(GL_CULL_FACE);

			float h1 = map->getCell(renderPos.x, renderPos.y)->getHeight();
			float h2 = map->getCell(renderPos.x, renderPos.y+1)->getHeight();
			float h3 = map->getCell(renderPos.x+1, renderPos.y)->getHeight();
			float h4 = map->getCell(renderPos.x+1, renderPos.y+1)->getHeight();

			glBegin(GL_TRIANGLE_STRIP);
			glVertex3f(
				static_cast<float>(renderPos.x),
				h1,
				static_cast<float>(renderPos.y));
			glVertex3f(
				static_cast<float>(renderPos.x),
				h2,
				static_cast<float>(renderPos.y+1));
			glVertex3f(
				static_cast<float>(renderPos.x+1),
				h3,
				static_cast<float>(renderPos.y));
			glVertex3f(
				static_cast<float>(renderPos.x+1),
				h4,
				static_cast<float>(renderPos.y+1));
			glEnd();

			glPopName();
			glPopName();
		}
	}

	glPopMatrix();
}

void Renderer::renderQuad(int x, int y, int w, int h, const Texture2D *texture) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}
	if(texture == NULL) {
		printf("\n**WARNING** detected a null texture to render in renderQuad!\n");
		return;
	}

	if(w < 0) {
		w = texture->getPixmapConst()->getW();
	}
	if(h < 0) {
		h = texture->getPixmapConst()->getH();
	}

	glBindTexture(GL_TEXTURE_2D, static_cast<const Texture2DGl*>(texture)->getHandle());
	glBegin(GL_TRIANGLE_STRIP);
		glTexCoord2i(0, 1);
		glVertex2i(x, y+h);
		glTexCoord2i(0, 0);
		glVertex2i(x, y);
		glTexCoord2i(1, 1);
		glVertex2i(x+w, y+h);
		glTexCoord2i(1, 0);
		glVertex2i(x+w, y);
	glEnd();
}

Renderer::Shadows Renderer::strToShadows(const string &s){
	if(s=="Projected"){
		return sProjected;
	}
	else if(s=="ShadowMapping"){
		return sShadowMapping;
	}
	return sDisabled;
}

string Renderer::shadowsToStr(Shadows shadows){
	switch(shadows){
	case sDisabled:
		return "Disabled2";
	case sProjected:
		return "Projected";
	case sShadowMapping:
		return "ShadowMapping";
	default:
		assert(false);
		return "";
	}
}

Texture2D::Filter Renderer::strToTextureFilter(const string &s){
	if(s=="Bilinear"){
		return Texture2D::fBilinear;
	}
	else if(s=="Trilinear"){
		return Texture2D::fTrilinear;
	}

	throw megaglest_runtime_error("Error converting from string to FilterType, found: "+s);
}

void Renderer::setAllowRenderUnitTitles(bool value) {
	allowRenderUnitTitles = value;
}

// This method renders titles for units
void Renderer::renderUnitTitles3D(Font3D *font, Vec3f color) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	//std::map<int,bool> unitRenderedList;

	if(visibleFrameUnitList.empty() == false) {
		//printf("Render Unit titles ON\n");

		for(int idx = 0; idx < (int)visibleFrameUnitList.size(); idx++) {
			const Unit *unit = visibleFrameUnitList[idx];
			if(unit != NULL) {
				if(unit->getVisible() == true) {
					if(unit->getCurrentUnitTitle() != "") {
						//get the screen coordinates
						Vec3f screenPos = unit->getScreenPos();
						renderText3D(unit->getCurrentUnitTitle(), font, color, std::fabs(screenPos.x) + 5, std::fabs(screenPos.y) + 5, false);
						//unitRenderedList[unit->getId()] = true;
					}
					else {
						string str = unit->getFullName(unit->showTranslatedTechTree()) + " - " + intToStr(unit->getId()) + " [" + unit->getPosNotThreadSafe().getString() + "]";
						Vec3f screenPos = unit->getScreenPos();
						renderText3D(str, font, color, std::fabs(screenPos.x) + 5, std::fabs(screenPos.y) + 5, false);
					}
				}
			}
		}
		visibleFrameUnitList.clear();
	}

	/*
	if(renderUnitTitleList.empty() == false) {
		for(int idx = 0; idx < renderUnitTitleList.size(); idx++) {
			std::pair<Unit *,Vec3f> &unitInfo = renderUnitTitleList[idx];
			Unit *unit = unitInfo.first;

			const World *world= game->getWorld();
			Unit *validUnit = world->findUnitById(unit->getId());

			if(validUnit != NULL && unitRenderedList.find(validUnit->getId()) == unitRenderedList.end()) {
				string str = validUnit->getFullName() + " - " + intToStr(validUnit->getId());
				//get the screen coordinates
				Vec3f &screenPos = unitInfo.second;
				renderText(str, font, color, fabs(screenPos.x) + 5, fabs(screenPos.y) + 5, false);
				//SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] screenPos.x = %f, screenPos.y = %f, screenPos.z = %f\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,screenPos.x,screenPos.y,screenPos.z);
			}
		}
		renderUnitTitleList.clear();
	}
	*/
}

// This method renders titles for units
void Renderer::renderUnitTitles(Font2D *font, Vec3f color) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	//std::map<int,bool> unitRenderedList;

	if(visibleFrameUnitList.empty() == false) {
		//printf("Render Unit titles ON\n");

		for(int idx = 0; idx < (int)visibleFrameUnitList.size(); idx++) {
			const Unit *unit = visibleFrameUnitList[idx];
			if(unit != NULL) {
				if(unit->getCurrentUnitTitle() != "") {
					//get the screen coordinates
					Vec3f screenPos = unit->getScreenPos();
					renderText(unit->getCurrentUnitTitle(), font, color, std::fabs(screenPos.x) + 5, std::fabs(screenPos.y) + 5, false);

					//unitRenderedList[unit->getId()] = true;
				}
				else {
					string str = unit->getFullName(unit->showTranslatedTechTree()) + " - " + intToStr(unit->getId()) + " [" + unit->getPosNotThreadSafe().getString() + "]";
					Vec3f screenPos = unit->getScreenPos();
					renderText(str, font, color, std::fabs(screenPos.x) + 5, std::fabs(screenPos.y) + 5, false);
				}
			}
		}
		visibleFrameUnitList.clear();
	}

	/*
	if(renderUnitTitleList.empty() == false) {
		for(int idx = 0; idx < renderUnitTitleList.size(); idx++) {
			std::pair<Unit *,Vec3f> &unitInfo = renderUnitTitleList[idx];
			Unit *unit = unitInfo.first;

			const World *world= game->getWorld();
			Unit *validUnit = world->findUnitById(unit->getId());

			if(validUnit != NULL && unitRenderedList.find(validUnit->getId()) == unitRenderedList.end()) {
				string str = validUnit->getFullName() + " - " + intToStr(validUnit->getId());
				//get the screen coordinates
				Vec3f &screenPos = unitInfo.second;
				renderText(str, font, color, fabs(screenPos.x) + 5, fabs(screenPos.y) + 5, false);
				//SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] screenPos.x = %f, screenPos.y = %f, screenPos.z = %f\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,screenPos.x,screenPos.y,screenPos.z);
			}
		}
		renderUnitTitleList.clear();
	}
	*/
}

void Renderer::removeObjectFromQuadCache(const Object *o) {
	VisibleQuadContainerCache &qCache = getQuadCache();
	for(int visibleIndex = 0;
			visibleIndex < (int)qCache.visibleObjectList.size(); ++visibleIndex) {
		Object *currentObj = qCache.visibleObjectList[visibleIndex];
		if(currentObj == o) {
			qCache.visibleObjectList.erase(qCache.visibleObjectList.begin() + visibleIndex);
			break;
		}
	}
}

void Renderer::removeUnitFromQuadCache(const Unit *unit) {
	VisibleQuadContainerCache &qCache = getQuadCache();
	for(int visibleIndex = 0;
			visibleIndex < (int)qCache.visibleQuadUnitList.size(); ++visibleIndex) {
		Unit *currentUnit = qCache.visibleQuadUnitList[visibleIndex];
		if(currentUnit == unit) {
			qCache.visibleQuadUnitList.erase(qCache.visibleQuadUnitList.begin() + visibleIndex);
			break;
		}
	}
	for(int visibleIndex = 0;
			visibleIndex < (int)qCache.visibleUnitList.size(); ++visibleIndex) {
		Unit *currentUnit = qCache.visibleUnitList[visibleIndex];
		if(currentUnit == unit) {
			qCache.visibleUnitList.erase(qCache.visibleUnitList.begin() + visibleIndex);
			break;
		}
	}
}

VisibleQuadContainerCache & Renderer::getQuadCache(	bool updateOnDirtyFrame,
													bool forceNew) {
	//forceNew = true;
	if(game != NULL && game->getWorld() != NULL) {
		const World *world= game->getWorld();

		if(forceNew == true ||
			(updateOnDirtyFrame == true &&
			(world->getFrameCount() != quadCache.cacheFrame ||
			 visibleQuad != quadCache.lastVisibleQuad))) {

			// Dump cached info
			//if(forceNew == true || visibleQuad != quadCache.lastVisibleQuad) {
			//quadCache.clearCacheData();
			//}
			//else {
			quadCache.clearVolatileCacheData();
			worldToScreenPosCache.clear();
			//}

			// Unit calculations
			for(int i = 0; i < world->getFactionCount(); ++i) {
				const Faction *faction = world->getFaction(i);
				for(int j = 0; j < faction->getUnitCount(); ++j) {
					Unit *unit= faction->getUnit(j);

					bool unitCheckedForRender = false;
					if(VisibleQuadContainerCache::enableFrustumCalcs == true) {
						//bool insideQuad 	= PointInFrustum(quadCache.frustumData, unit->getCurrVector().x, unit->getCurrVector().y, unit->getCurrVector().z );
						bool insideQuad 	= CubeInFrustum(quadCache.frustumData, unit->getCurrMidHeightVector().x, unit->getCurrMidHeightVector().y, unit->getCurrMidHeightVector().z, unit->getType()->getRenderSize());
						bool renderInMap 	= world->toRenderUnit(unit);
						if(insideQuad == false || renderInMap == false) {
							unit->setVisible(false);
							if(renderInMap == true) {
								quadCache.visibleUnitList.push_back(unit);
							}
							unitCheckedForRender = true; // no more need to check any further;
							// Currently don't need this list
							//quadCache.inVisibleUnitList.push_back(unit);
						}
					}
					if(unitCheckedForRender == false) {
						bool insideQuad 	= visibleQuad.isInside(unit->getPos());
						bool renderInMap 	= world->toRenderUnit(unit);
						if(insideQuad == true && renderInMap == true) {
							quadCache.visibleQuadUnitList.push_back(unit);
						}
						else {
							unit->setVisible(false);
							// Currently don't need this list
							//quadCache.inVisibleUnitList.push_back(unit);
						}

						if(renderInMap == true) {
							quadCache.visibleUnitList.push_back(unit);
						}
					}

					bool unitBuildPending = unit->isBuildCommandPending();
					if(unitBuildPending == true) {
						const UnitBuildInfo &pendingUnit = unit->getBuildCommandPendingInfo();
						const Vec2i &pos = pendingUnit.pos;
						const Map *map= world->getMap();

						bool unitBuildCheckedForRender = false;

						//printf("#1 Unit is about to build another unit\n");

						if(VisibleQuadContainerCache::enableFrustumCalcs == true) {
							Vec3f pos3f= Vec3f(pos.x, map->getCell(pos)->getHeight(), pos.y);
							//bool insideQuad 	= PointInFrustum(quadCache.frustumData, unit->getCurrVector().x, unit->getCurrVector().y, unit->getCurrVector().z );
							bool insideQuad 	= CubeInFrustum(quadCache.frustumData, pos3f.x, pos3f.y, pos3f.z, pendingUnit.buildUnit->getRenderSize());
							bool renderInMap 	= world->toRenderUnit(pendingUnit);
							if(insideQuad == false || renderInMap == false) {
								if(renderInMap == true) {
									quadCache.visibleQuadUnitBuildList.push_back(pendingUnit);
								}
								unitBuildCheckedForRender = true; // no more need to check any further;
								// Currently don't need this list
								//quadCache.inVisibleUnitList.push_back(unit);
							}

							//printf("#2 Unit build added? insideQuad = %d, renderInMap = %d\n",insideQuad,renderInMap);
						}

						if(unitBuildCheckedForRender == false) {
							bool insideQuad 	= visibleQuad.isInside(pos);
							bool renderInMap 	= world->toRenderUnit(pendingUnit);
							if(insideQuad == true && renderInMap == true) {
								quadCache.visibleQuadUnitBuildList.push_back(pendingUnit);
							}
							else {
								//unit->setVisible(false);
								// Currently don't need this list
								//quadCache.inVisibleUnitList.push_back(unit);
							}

							//printf("#3 Unit build added? insideQuad = %d, renderInMap = %d\n",insideQuad,renderInMap);
						}

						//printf("#4 quadCache.visibleQuadUnitBuildList.size() = %d\n",quadCache.visibleQuadUnitBuildList.size());
					}
				}
			}

			if(forceNew == true || visibleQuad != quadCache.lastVisibleQuad) {
				// Object calculations
				const Map *map= world->getMap();
				// clear visibility of old objects
				for(int visibleIndex = 0;
					visibleIndex < (int)quadCache.visibleObjectList.size(); ++visibleIndex){
					quadCache.visibleObjectList[visibleIndex]->setVisible(false);
				}
				quadCache.clearNonVolatileCacheData();

				//int loops1=0;
				PosQuadIterator pqi(map,visibleQuad, Map::cellScale);
				while(pqi.next()) {
					const Vec2i &pos= pqi.getPos();
					if(map->isInside(pos)) {
						//loops1++;
						const Vec2i &mapPos = Map::toSurfCoords(pos);

						//quadCache.visibleCellList.push_back(mapPos);

						SurfaceCell *sc = map->getSurfaceCell(mapPos);
						Object *o = sc->getObject();

						if(VisibleQuadContainerCache::enableFrustumCalcs == true) {
							if(o != NULL) {
								//bool insideQuad 	= PointInFrustum(quadCache.frustumData, o->getPos().x, o->getPos().y, o->getPos().z );
								bool insideQuad 	= CubeInFrustum(quadCache.frustumData, o->getPos().x, o->getPos().y, o->getPos().z, 1);
								if(insideQuad == false) {
									o->setVisible(false);
									continue;
								}
							}
						}

                        bool cellExplored = world->showWorldForPlayer(world->getThisFactionIndex());
                        if(cellExplored == false) {
                            cellExplored = sc->isExplored(world->getThisTeamIndex());
                        }

						bool isExplored = (cellExplored == true && o != NULL);
						//bool isVisible = (sc->isVisible(world->getThisTeamIndex()) && o != NULL);
						bool isVisible = true;

						if(isExplored == true && isVisible == true) {
							quadCache.visibleObjectList.push_back(o);
							o->setVisible(true);
						}
					}
				}

				//printf("Frame # = %d loops1 = %d\n",world->getFrameCount(),loops1);

				//int loops2=0;

				std::map<Vec2i, MarkedCell> markedCells = game->getMapMarkedCellList();

				const Rect2i mapBounds(0, 0, map->getSurfaceW()-1, map->getSurfaceH()-1);
				Quad2i scaledQuad = visibleQuad / Map::cellScale;
				PosQuadIterator pqis(map,scaledQuad);
				while(pqis.next()) {
					const Vec2i &pos= pqis.getPos();
					if(mapBounds.isInside(pos)) {
						//loops2++;
						if(VisibleQuadContainerCache::enableFrustumCalcs == false) {
							quadCache.visibleScaledCellList.push_back(pos);

							if(markedCells.empty() == false) {
								if(markedCells.find(pos) != markedCells.end()) {
									//printf("#1 ******** VISIBLE SCALED CELL FOUND in marked list pos [%s] markedCells.size() = " MG_SIZE_T_SPECIFIER "\n",pos.getString().c_str(),markedCells.size());
								//if(markedCells.empty() == false) {
									//SurfaceCell *sc = map->getSurfaceCell(pos);
									//quadCache.visibleScaledCellToScreenPosList[pos]=computeScreenPosition(sc->getVertex());
									updateMarkedCellScreenPosQuadCache(pos);
								}
								else {
									//printf("#1 VISIBLE SCALED CELL NOT FOUND in marked list pos [%s] markedCells.size() = " MG_SIZE_T_SPECIFIER "\n",pos.getString().c_str(),markedCells.size());
								}
							}
						}
						else {
							bool insideQuad = false;

							if( !insideQuad) {
								SurfaceCell *sc = map->getSurfaceCell(pos.x, pos.y);
								insideQuad = CubeInFrustum(quadCache.frustumData, sc->getVertex().x, sc->getVertex().y, sc->getVertex().z, 0);
							}
							if( !insideQuad) {
								SurfaceCell *sc = map->getSurfaceCell(pos.x+1, pos.y);
								insideQuad = CubeInFrustum(quadCache.frustumData, sc->getVertex().x, sc->getVertex().y, sc->getVertex().z, 0);
							}
							if( !insideQuad) {
								SurfaceCell *sc = map->getSurfaceCell(pos.x, pos.y+1);
								insideQuad = CubeInFrustum(quadCache.frustumData, sc->getVertex().x, sc->getVertex().y, sc->getVertex().z, 0);
							}
							if( !insideQuad) {
								SurfaceCell *sc = map->getSurfaceCell(pos.x+1, pos.y+1);
								insideQuad = CubeInFrustum(quadCache.frustumData, sc->getVertex().x, sc->getVertex().y, sc->getVertex().z, 0);
							}

							if(insideQuad == true) {
								quadCache.visibleScaledCellList.push_back(pos);

								if(markedCells.empty() == false) {
									if(markedCells.find(pos) != markedCells.end()) {
										//printf("#2 ******** VISIBLE SCALED CELL FOUND in marked list pos [%s] markedCells.size() = " MG_SIZE_T_SPECIFIER "\n",pos.getString().c_str(),markedCells.size());
									//if(markedCells.empty() == false) {
										//quadCache.visibleScaledCellToScreenPosList[pos]=computeScreenPosition(sc->getVertex());
										updateMarkedCellScreenPosQuadCache(pos);
									}
									else {
										//printf("#2 VISIBLE SCALED CELL NOT FOUND in marked list pos [%s] markedCells.size() = " MG_SIZE_T_SPECIFIER "\n",pos.getString().c_str(),markedCells.size());
									}
								}
							}
						}
					}
				}
				//printf("Frame # = %d loops2 = %d\n",world->getFrameCount(),loops2);
			}
			quadCache.cacheFrame = world->getFrameCount();
			quadCache.lastVisibleQuad = visibleQuad;
		}
	}

	return quadCache;
}

void Renderer::updateMarkedCellScreenPosQuadCache(Vec2i pos) {
	const World *world= game->getWorld();
	const Map *map= world->getMap();

	SurfaceCell *sc = map->getSurfaceCell(pos);
	quadCache.visibleScaledCellToScreenPosList[pos]=computeScreenPosition(sc->getVertex());
}

void Renderer::forceQuadCacheUpdate() {
	quadCache.cacheFrame = -1;

	Vec2i clearPos(-1,-1);
	quadCache.lastVisibleQuad.p[0] = clearPos;
	quadCache.lastVisibleQuad.p[1] = clearPos;
	quadCache.lastVisibleQuad.p[2] = clearPos;
	quadCache.lastVisibleQuad.p[3] = clearPos;
}

std::pair<bool,Vec3f> Renderer::posInCellQuadCache(Vec2i pos) {
	std::pair<bool,Vec3f> result = make_pair(false,Vec3f());
	if(std::find(
			quadCache.visibleScaledCellList.begin(),
			quadCache.visibleScaledCellList.end(),
			pos) != quadCache.visibleScaledCellList.end()) {
		result.first = true;
		result.second = quadCache.visibleScaledCellToScreenPosList[pos];
	}
	return result;
}

Vec3f Renderer::getMarkedCellScreenPosQuadCache(Vec2i pos) {
	Vec3f result(-1,-1,-1);
	if(std::find(
			quadCache.visibleScaledCellList.begin(),
			quadCache.visibleScaledCellList.end(),
			pos) != quadCache.visibleScaledCellList.end()) {
		result = quadCache.visibleScaledCellToScreenPosList[pos];
	}
	return result;
}

void Renderer::beginRenderToTexture(Texture2D **renderToTexture) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	static bool supportFBOs = Texture2DGl().supports_FBO_RBO();

	if(supportFBOs == true && renderToTexture != NULL) {
		Config &config= Config::getInstance();
		Texture2D::Filter textureFilter = strToTextureFilter(config.getString("Filter"));
		int maxAnisotropy				= config.getInt("FilterMaxAnisotropy");

		const Metrics &metrics	= Metrics::getInstance();

		*renderToTexture = GraphicsInterface::getInstance().getFactory()->newTexture2D();
		Texture2DGl *texture = static_cast<Texture2DGl *>(*renderToTexture);
		texture->setMipmap(false);
		Pixmap2D *pixmapScreenShot = texture->getPixmap();
		pixmapScreenShot->init(metrics.getScreenW(), metrics.getScreenH(), 4);
		texture->setForceCompressionDisabled(true);
		texture->init(textureFilter,maxAnisotropy);
		texture->setup_FBO_RBO();

		assertGl();

		if(texture->checkFrameBufferStatus() == false) {
			//printf("******************** WARNING CANNOT Attach to FBO!\n");
			texture->end();
			delete texture;
			*renderToTexture=NULL;
		}
	}
}

void Renderer::endRenderToTexture(Texture2D **renderToTexture) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	static bool supportFBOs = Texture2DGl().supports_FBO_RBO();

	if(supportFBOs == true && renderToTexture != NULL && *renderToTexture != NULL) {
		Texture2DGl *texture = static_cast<Texture2DGl *>(*renderToTexture);
		if(texture != NULL) {
			texture->dettachFrameBufferFromTexture();
		}

		assertGl();
	}
}

void Renderer::renderMapPreview( const MapPreview *map, bool renderAll,
								 int screenPosX, int screenPosY,
								 Texture2D **renderToTexture) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	static bool supportFBOs = Texture2DGl().supports_FBO_RBO();
	if(Config::getInstance().getBool("LegacyMapPreviewRendering","false") == true) {
		supportFBOs = false;
	}

	//static bool supportFBOs = false;
	const Metrics &metrics= Metrics::getInstance();

	float alt				= 0;
	float showWater			= 0;
	int renderMapHeight		= 64;
	int renderMapWidth		= 64;
	float cellSize			= 2;
	float playerCrossSize	= 2;
	float clientW			= renderMapWidth * cellSize;
	float clientH			= renderMapHeight * cellSize;;
	float minDimension 		= std::min(metrics.getVirtualW(), metrics.getVirtualH());

	// stretch small maps to 128x128
	if(map->getW() < map->getH()) {
		cellSize = cellSize * renderMapHeight / map->getH();
	}
	else {
		cellSize = cellSize * renderMapWidth / map->getW();
	}

	assertGl();

	if(supportFBOs == true && renderToTexture != NULL) {
		Config &config= Config::getInstance();
		Texture2D::Filter textureFilter = strToTextureFilter(config.getString("Filter"));
		int maxAnisotropy				= config.getInt("FilterMaxAnisotropy");

		*renderToTexture = GraphicsInterface::getInstance().getFactory()->newTexture2D();
		Texture2DGl *texture = static_cast<Texture2DGl *>(*renderToTexture);
		texture->setMipmap(false);
		Pixmap2D *pixmapScreenShot = texture->getPixmap();
		pixmapScreenShot->init(minDimension, minDimension, 4);
		texture->setForceCompressionDisabled(true);
		texture->init(textureFilter,maxAnisotropy);
		texture->setup_FBO_RBO();

		assertGl();

		if(texture->checkFrameBufferStatus() == false) {
			//printf("******************** WARNING CANNOT Attach to FBO!\n");
			texture->end();
			delete texture;
			*renderToTexture=NULL;
		}
	}

	if(supportFBOs == true && renderToTexture != NULL && *renderToTexture != NULL) {
		cellSize  =1;
		clientW = minDimension;
		clientH = minDimension;
		int mapMaxDimensionSize = std::max(map->getW(),map->getH());
		switch(mapMaxDimensionSize) {
			case 8:
				cellSize = 96;
				break;
			case 16:
				cellSize = 48;
				break;
			case 32:
				cellSize = 24;
				break;
			case 64:
				cellSize = 12;
				break;
			case 128:
				cellSize = 6;
				break;
			case 256:
				cellSize = 3;
				break;
			case 512:
				cellSize = 2;
				break;
		}
	}

	glFrontFace(GL_CW);
	glEnable(GL_CULL_FACE);

	glMatrixMode(GL_PROJECTION);
    glPushMatrix();
	glLoadIdentity();

	assertGl();

	GLint viewport[4];	// Where The original Viewport Values Will Be Stored

	if(supportFBOs == true && renderToTexture != NULL && *renderToTexture != NULL) {
		glGetIntegerv(GL_VIEWPORT, viewport);
		glOrtho(0, clientW, 0, clientH, 0, 1);
		glViewport(0, 0, clientW, clientH);
	}
	else {
		glOrtho(0, metrics.getVirtualW(), 0, metrics.getVirtualH(), 0, 1);
	}

	glMatrixMode(GL_MODELVIEW);

	glPushMatrix();
	glLoadIdentity();

	if(supportFBOs == false || renderToTexture == NULL ||  *renderToTexture == NULL) {
		glTranslatef(static_cast<float>(screenPosX),static_cast<float>(screenPosY)-clientH,0.0f);
	}

	assertGl();

	glPushAttrib(GL_CURRENT_BIT);
	glLineWidth(1);
	glColor3f(0, 0, 0);

	for (int j = 0; j < map->getH(); j++) {
		for (int i = 0; i < map->getW(); i++) {
			//surface
			alt = map->getHeight(i, j) / 20.f;
			showWater = map->getWaterLevel()/ 20.f - alt;
			showWater = (showWater > 0)? showWater:0;
			Vec3f surfColor;
			switch (map->getSurface(i, j)) {
				case st_Grass:
					surfColor = Vec3f(0.0, 0.8f * alt, 0.f + showWater);
					break;
				case st_Secondary_Grass:
					surfColor = Vec3f(0.4f * alt, 0.6f * alt, 0.f + showWater);
					break;
				case st_Road:
					surfColor = Vec3f(0.6f * alt, 0.3f * alt, 0.f + showWater);
					break;
				case st_Stone:
					surfColor = Vec3f(0.7f * alt, 0.7f * alt, 0.7f * alt + showWater);
					break;
				case st_Ground:
					surfColor = Vec3f(0.7f * alt, 0.5f * alt, 0.3f * alt + showWater);
					break;
			}

			glColor3fv(surfColor.ptr());

			glBegin(GL_TRIANGLE_STRIP);
			glVertex2f(i * cellSize, clientH - j * cellSize - cellSize);
			glVertex2f(i * cellSize, clientH - j * cellSize);
			glVertex2f(i * cellSize + cellSize, clientH - j * cellSize - cellSize);
			glVertex2f(i * cellSize + cellSize, clientH - j * cellSize);
			glEnd();

			//objects
			if(renderAll == true) {
				switch (map->getObject(i, j)) {
					case 0:
						glColor3f(0.f, 0.f, 0.f);
						break;
					case 1:
						glColor3f(1.f, 0.f, 0.f);
						break;
					case 2:
						glColor3f(1.f, 1.f, 1.f);
						break;
					case 3:
						glColor3f(0.5f, 0.5f, 1.f);
						break;
					case 4:
						glColor3f(0.f, 0.f, 1.f);
						break;
					case 5:
						glColor3f(0.5f, 0.5f, 0.5f);
						break;
					case 6:
						glColor3f(1.f, 0.8f, 0.5f);
						break;
					case 7:
						glColor3f(0.f, 1.f, 1.f);
						break;
					case 8:
						glColor3f(0.7f, 0.1f, 0.3f);
						break;
					case 9:
						glColor3f(0.5f, 1.f, 0.1f);
						break;
					case 10:
						glColor3f(1.f, 0.2f, 0.8f);
						break;// we don't render unvisible blocking objects
				}

				if ( renderAll && (map->getObject(i, j) != 0) && (map->getObject(i, j) != 10) ) {
					glPointSize(cellSize / 2.f);
					glBegin(GL_POINTS);
					glVertex2f(i * cellSize + cellSize / 2.f, clientH - j * cellSize - cellSize / 2.f);
					glEnd();
				}
			}

	//		bool found = false;

			//height lines
	//		if (!found) {

				//left
				if (i > 0 && map->getHeight(i - 1, j) > map->getHeight(i, j)) {
					glColor3fv((surfColor*0.5f).ptr());
					glBegin(GL_LINES);
					glVertex2f(i * cellSize, clientH - (j + 1) * cellSize);
					glVertex2f(i * cellSize, clientH - j * cellSize);
					glEnd();
				}
				//down
				if (j > 0 && map->getHeight(i, j - 1) > map->getHeight(i, j)) {
					glColor3fv((surfColor*0.5f).ptr());
					glBegin(GL_LINES);
					glVertex2f(i * cellSize, clientH - j * cellSize);
					glVertex2f((i + 1) * cellSize, clientH - j * cellSize);
					glEnd();
				}


				//left
				if (i > 0 && map->getHeight(i - 1, j) < map->getHeight(i, j)) {
					glColor3fv((surfColor*2.f).ptr());
					glBegin(GL_LINES);
					glVertex2f(i * cellSize, clientH - (j + 1)  * cellSize);
					glVertex2f(i * cellSize, clientH - j * cellSize);
					glEnd();
				}
				if (j > 0 && map->getHeight(i, j - 1) < map->getHeight(i, j)) {
					glColor3fv((surfColor*2.f).ptr());
					glBegin(GL_LINES);
					glVertex2f(i * cellSize, clientH - j * cellSize);
					glVertex2f((i + 1) * cellSize, clientH - j * cellSize);
					glEnd();
				}
	//				}

				//resources
				if(renderAll == true) {
					switch (map->getResource(i, j)) {
						case 1: glColor3f(1.f, 1.f, 0.f); break;
						case 2: glColor3f(0.5f, 0.5f, 0.5f); break;
						case 3: glColor3f(1.f, 0.f, 0.f); break;
						case 4: glColor3f(0.f, 0.f, 1.f); break;
						case 5: glColor3f(0.5f, 0.5f, 1.f); break;
					}

					if (renderAll && map->getResource(i, j) != 0) {
						glBegin(GL_LINES);
						glVertex2f(i * cellSize, clientH - j * cellSize - cellSize);
						glVertex2f(i * cellSize + cellSize, clientH - j * cellSize);
						glVertex2f(i * cellSize, clientH - j * cellSize);
						glVertex2f(i * cellSize + cellSize, clientH - j * cellSize - cellSize);
						glEnd();
					}
				}
		}
	}

	//start locations
	glLineWidth(3);

	assertGl();

	if(supportFBOs == true && renderToTexture != NULL && *renderToTexture != NULL) {
		glLineWidth(14);
		playerCrossSize = 24;
	}
	else {
		// force playerCrossSize to be at least of size 4
		if(cellSize < 4) {
			playerCrossSize = 4;
		}
		else {
			playerCrossSize = cellSize;
		}
	}

	assertGl();

	Vec2f *vertices = new Vec2f[map->getMaxFactions() * 4];
	Vec3f *colors = new Vec3f[map->getMaxFactions() * 4];

	for (int i = 0; i < map->getMaxFactions(); i++) {
		Vec3f color;
		switch (i) {
			case 0:
				color = Vec3f(1.f, 0.f, 0.f);
				break;
			case 1:
				color = Vec3f(0.f, 0.f, 1.f);
				break;
			case 2:
				color = Vec3f(0.f, 1.f, 0.f);
				break;
			case 3:
				color = Vec3f(1.f, 1.f, 0.f);
				break;
			case 4:
				color = Vec3f(1.f, 1.f, 1.f);
				break;
			case 5:
				color = Vec3f(0.f, 1.f, 0.8f);
				break;
			case 6:
				color = Vec3f(1.f, 0.5f, 0.f);
				break;
			case 7:
				color = Vec3f(1.f, 0.5f, 1.f);
				break;
   		}

		colors[i*4]     = color;
		colors[(i*4)+1] = color;
		colors[(i*4)+2] = color;
		colors[(i*4)+3] = color;

		vertices[i*4] 	= Vec2f((map->getStartLocationX(i) - 1) * cellSize, clientH - (map->getStartLocationY(i) - 1) * cellSize);
		vertices[(i*4)+1] = Vec2f((map->getStartLocationX(i) + 1) * cellSize + playerCrossSize, clientH - (map->getStartLocationY(i) + 1) * cellSize - playerCrossSize);
		vertices[(i*4)+2] = Vec2f((map->getStartLocationX(i) - 1) * cellSize, clientH - (map->getStartLocationY(i) + 1) * cellSize - playerCrossSize);
		vertices[(i*4)+3] = Vec2f((map->getStartLocationX(i) + 1) * cellSize + playerCrossSize, clientH - (map->getStartLocationY(i) - 1) * cellSize);
	}

	glEnableClientState(GL_COLOR_ARRAY);
	glColorPointer(3, GL_FLOAT, 0, &colors[0]);
	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(2, GL_FLOAT, 0, &vertices[0]);
	glDrawArrays(GL_LINES, 0, 4 * map->getMaxFactions());
	glDisableClientState(GL_COLOR_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);

	assertGl();

	glLineWidth(1);

	glPopMatrix();
	glPopAttrib();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();

	if(supportFBOs == true && renderToTexture != NULL && *renderToTexture != NULL) {
		Texture2DGl *texture = static_cast<Texture2DGl *>(*renderToTexture);
		if(texture != NULL) {
			texture->dettachFrameBufferFromTexture();
		}

		glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);

		assertGl();
	}

	delete [] vertices;
	delete [] colors;

	assertGl();
}

// setLastRenderFps and calculate shadowsOffDueToMinRender
void Renderer::setLastRenderFps(int value) {
	 	lastRenderFps = value;
	 	smoothedRenderFps=(MIN_FPS_NORMAL_RENDERING*smoothedRenderFps+lastRenderFps)/(MIN_FPS_NORMAL_RENDERING+1.0f);

		if(smoothedRenderFps>=MIN_FPS_NORMAL_RENDERING_TOP_THRESHOLD){
			shadowsOffDueToMinRender=false;
		}
		if(smoothedRenderFps<=MIN_FPS_NORMAL_RENDERING){
			 shadowsOffDueToMinRender=true;
		}
}

std::size_t Renderer::getCurrentPixelByteCount(ResourceScope rs) const {
	std::size_t result = 0;
	for(int i = (rs == rsCount ? 0 : rs); i < rsCount; ++i) {
		if(textureManager[i] != NULL) {
			const ::Shared::Graphics::TextureContainer &textures = textureManager[i]->getTextures();
			for(int j = 0; j < (int)textures.size(); ++j) {
				const Texture *texture = textures[j];
				result += texture->getPixelByteCount();
			}
			if(rs != rsCount) {
				break;
			}
		}
	}

	return result;
}

Texture2D * Renderer::preloadTexture(string logoFilename) {
	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] logoFilename [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,logoFilename.c_str());

	Texture2D *result = NULL;
	if(logoFilename != "") {
		// Cache faction preview textures
		string data_path = getGameReadWritePath(GameConstants::path_data_CacheLookupKey);
		std::map<string,Texture2D *> &crcFactionPreviewTextureCache = CacheManager::getCachedItem< std::map<string,Texture2D *> >(GameConstants::factionPreviewTextureCacheLookupKey);

		if(crcFactionPreviewTextureCache.find(logoFilename) != crcFactionPreviewTextureCache.end()) {
			if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] logoFilename [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,logoFilename.c_str());

			if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s Line: %d] load texture from cache [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,logoFilename.c_str());

			result = crcFactionPreviewTextureCache[logoFilename];
		}
		else {
			if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] logoFilename [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,logoFilename.c_str());
			Renderer &renderer= Renderer::getInstance();
			result = renderer.newTexture2D(rsGlobal);
			if(result) {
				result->setMipmap(true);
				result->load(logoFilename);
				//renderer.initTexture(rsGlobal,result);
			}

			if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s Line: %d] add texture to manager and cache [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,logoFilename.c_str());

			crcFactionPreviewTextureCache[logoFilename] = result;
		}
	}

	return result;
}

Texture2D * Renderer::findTexture(string logoFilename) {
	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] logoFilename [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,logoFilename.c_str());

	Texture2D *result = preloadTexture(logoFilename);
	if(result != NULL && result->getInited() == false) {
		Renderer &renderer= Renderer::getInstance();
		renderer.initTexture(rsGlobal,result);
	}

	return result;
}

void Renderer::cycleShowDebugUILevel() {
	//printf("#1 showDebugUILevel = %d, debugui_fps = %d, debugui_unit_titles = %d\n",showDebugUILevel,debugui_fps,debugui_unit_titles);

	//if(showDebugUI == false) {
	//	showDebugUI = true;
	//}
	if((showDebugUILevel & debugui_fps) != debugui_fps ||
		(showDebugUILevel & debugui_unit_titles) != debugui_unit_titles) {
		showDebugUILevel  |= debugui_fps;
		showDebugUILevel  |= debugui_unit_titles;
	}
	else {
		showDebugUILevel  = debugui_fps;
	}

	//printf("#2 showDebugUILevel = %d, debugui_fps = %d, debugui_unit_titles = %d\n",showDebugUILevel,debugui_fps,debugui_unit_titles);
}

void Renderer::renderFPSWhenEnabled(int lastFps) {
	if(getShowDebugUI() == true) {
		CoreData &coreData= CoreData::getInstance();
		if(Renderer::renderText3DEnabled) {
			renderText3D(
				"FPS: " + intToStr(lastFps),
				coreData.getMenuFontNormal3D(), Vec3f(1.f), 10, 10, false);
		}
		else {
			renderText(
				"FPS: " + intToStr(lastFps),
				coreData.getMenuFontNormal(), Vec3f(1.f), 10, 10, false);
		}
	}
}

void Renderer::renderPopupMenu(PopupMenu *menu) {
	if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
		return;
	}

	if(menu->getVisible() == false || menu->getEnabled() == false) {
		return;
	}

	//background
	glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT);
	glEnable(GL_BLEND);

	glColor4f(0.0f, 0.0f, 0.0f, 0.8f) ;
	glBegin(GL_TRIANGLE_STRIP);
		glVertex2i(menu->getX(), menu->getY() + 9 * menu->getH() / 10);
		glVertex2i(menu->getX(), menu->getY());
		glVertex2i(menu->getX() + menu->getW(), menu->getY() + 9 * menu->getH() / 10);
		glVertex2i(menu->getX() + menu->getW(), menu->getY());
	glEnd();

	glColor4f(0.0f, 0.0f, 0.0f, 0.8f) ;
	glBegin(GL_TRIANGLE_STRIP);
		glVertex2i(menu->getX(), menu->getY() + menu->getH());
		glVertex2i(menu->getX(), menu->getY() + 9 * menu->getH() / 10);
		glVertex2i(menu->getX() + menu->getW(), menu->getY() + menu->getH());
		glVertex2i(menu->getX() + menu->getW(), menu->getY() + 9 * menu->getH() / 10);
	glEnd();

	glBegin(GL_LINE_LOOP);
		glColor4f(0.5f, 0.5f, 0.5f, 0.25f) ;
		glVertex2i(menu->getX(), menu->getY());

		glColor4f(0.0f, 0.0f, 0.0f, 0.25f) ;
		glVertex2i(menu->getX() + menu->getW(), menu->getY());

		glColor4f(0.5f, 0.5f, 0.5f, 0.25f) ;
		glVertex2i(menu->getX() + menu->getW(), menu->getY() + menu->getH());

		glColor4f(0.25f, 0.25f, 0.25f, 0.25f) ;
		glVertex2i(menu->getX(), menu->getY() + menu->getH());
	glEnd();

	glBegin(GL_LINE_STRIP);
		glColor4f(1.0f, 1.0f, 1.0f, 0.25f) ;
		glVertex2i(menu->getX(), menu->getY() + 90*menu->getH()/100);

		glColor4f(0.5f, 0.5f, 0.5f, 0.25f) ;
		glVertex2i(menu->getX()+ menu->getW(), menu->getY() + 90*menu->getH()/100);
	glEnd();

	glPopAttrib();

	Vec4f fontColor;
	//if(game!=NULL){
	//	fontColor=game->getGui()->getDisplay()->getColor();
	//}
	//else {
		// white shadowed is default ( in the menu for example )
		fontColor=Vec4f(1.f, 1.f, 1.f, 1.0f);
	//}

	if(renderText3DEnabled == true) {
		//text
		renderTextBoundingBox3D(
				menu->getHeader(), menu->getFont3D(),fontColor,
				menu->getX(), menu->getY()+93*menu->getH()/100,menu->getW(),0,
			true,false, false,-1,-1);

	}
	else {
		//text
		int renderX = (menu->getX() + (menu->getW() / 2));
		//int renderY = (menu->getY() + (menu->getH() / 2));
		FontMetrics *fontMetrics= menu->getFont()->getMetrics();
		int renderY = menu->getY() + menu->getH() - fontMetrics->getHeight(menu->getHeader());
		renderTextShadow(
				menu->getHeader(), menu->getFont(),fontColor,
				renderX, renderY,
			true);

		//renderText(button->getText(), button->getFont(), color,x + (w / 2), y + (h / 2), true);
	}

	//buttons
//	int maxButtonWidth = -1;
	std::vector<GraphicButton> &menuItems = menu->getMenuItems();
//	for(unsigned int i = 0; i < menuItems.size(); ++i) {
//		GraphicButton *button = &menuItems[i];
//		int currentButtonWidth = -1;
//		if(renderText3DEnabled == true) {
//			FontMetrics *fontMetrics= menu->getFont3D()->getMetrics();
//			currentButtonWidth = fontMetrics->getTextWidth(button->getText());
//		}
//		else {
//			FontMetrics *fontMetrics= menu->getFont()->getMetrics();
//			currentButtonWidth = fontMetrics->getTextWidth(button->getText());
//		}
//
//		if(maxButtonWidth < 0 || currentButtonWidth > maxButtonWidth) {
//			maxButtonWidth = currentButtonWidth + 5;
//		}
//	}

	for(unsigned int i = 0; i < menuItems.size(); ++i) {
		GraphicButton *button = &menuItems[i];

		//button->setW(maxButtonWidth);
		renderButton(button);
	}

}

void Renderer::setupRenderForVideo() {
	clearBuffers();
	//3d
	reset3dMenu();
	clearZBuffer();
	//2d
	reset2d();
	glClearColor(0.f, 0.f, 0.f, 1.f);
}

void Renderer::renderVideoLoading(int progressPercent) {
	//printf("Rendering progress progressPercent = %d\n",progressPercent);
	setupRenderForVideo();

	Lang &lang= Lang::getInstance();
	string textToRender = lang.getString("PleaseWait");
	const Metrics &metrics= Metrics::getInstance();

	static Chrono cycle(true);
	static float anim = 0.0f;

	if(CoreData::getInstance().getMenuFontBig3D() != NULL) {

		int w= metrics.getVirtualW();
		int renderX = (w / 2) - (CoreData::getInstance().getMenuFontBig3D()->getMetrics()->getTextWidth(textToRender) / 2);
		int h= metrics.getVirtualH();
		int renderY = (h / 2) + (CoreData::getInstance().getMenuFontBig3D()->getMetrics()->getHeight(textToRender) / 2);

		renderText3D(
				textToRender,
				CoreData::getInstance().getMenuFontBig3D(),
				Vec4f(1.f, 1.f, 0.f,anim),
				renderX, renderY, false);
	}
	else {
		renderText(
				textToRender,
				CoreData::getInstance().getMenuFontBig(),
				Vec4f(1.f, 1.f, 0.f,anim), (metrics.getScreenW() / 2),
				(metrics.getScreenH() / 2), true);
	}
    swapBuffers();

    if(cycle.getCurMillis() % 50 == 0) {
    	static bool animCycleUp = true;
    	if(animCycleUp == true) {
			anim += 0.1f;
			if(anim > 1.f) {
				anim= 1.f;
				cycle.reset();
				animCycleUp = false;
			}
    	}
    	else {
			anim -= 0.1f;
			if(anim < 0.f) {
				anim= 0.f;
				cycle.reset();
				animCycleUp = true;
			}
    	}
    }
}

}}//end namespace
