2025-07-16 13:36 CEST

View Issue Details Jump to Notes ]
IDProjectCategoryView StatusLast Update
0002393Spring engineGeneralpublic2011-05-31 23:47
Reporterabma 
Assigned Toabma 
PrioritynormalSeveritycrashReproducibilityalways
StatusresolvedResolutionfixed 
Product Version0.82.3+git 
Target VersionFixed in Version 
Summary0002393: (master) crash with "Red Build/Order Menu" and >300 Units when pressing CTRL+A (Out of Memory)
Descriptioni didn't test a long time, so this crashes maybe have nothing to do with assimp

crashes sometimes with the

/cheat
/give all
/give all 1

"test"
Additional Informationhttp://pastebin.com/sfQKiMMM

git describe
0.82.3-1685-gda562a5
TagsNo tags attached.
Checked infolog.txt for Errors
Attached Files
  • diff file icon git.diff (133,696 bytes) 2011-04-01 02:20 -
    diff --git a/rts/Game/Camera.cpp b/rts/Game/Camera.cpp
    index 3a13aae..aec1220 100644
    --- a/rts/Game/Camera.cpp
    +++ b/rts/Game/Camera.cpp
    @@ -132,9 +132,8 @@ void CCamera::Update(bool freeze, bool resetUp)
     
     	const float gndHeight = ground->GetHeightAboveWater(pos.x, pos.z);
     	const float rangemod = 1.0f + std::max(0.0f, pos.y - gndHeight - 500.0f) * 0.0003f;
    -	const float zNear = (CGlobalRendering::NEAR_PLANE * rangemod);
    -
    -	globalRendering->viewRange = CGlobalRendering::MAX_VIEW_RANGE * rangemod;
    +	const float zNear = (NEAR_PLANE * rangemod);
    +	globalRendering->viewRange = MAX_VIEW_RANGE * rangemod;
     
     	glMatrixMode(GL_PROJECTION);
     	glLoadIdentity();
    diff --git a/rts/Game/Game.cpp b/rts/Game/Game.cpp
    index 4e046dc..85ea86f 100644
    --- a/rts/Game/Game.cpp
    +++ b/rts/Game/Game.cpp
    @@ -217,6 +217,7 @@ CR_REG_METADATA(CGame,(
     
     
     CGame::CGame(const std::string& mapname, const std::string& modName, ILoadSaveHandler* saveFile) :
    +	finishedLoading(false),
     	gameDrawMode(gameNotDrawing),
     	defsParser(NULL),
     	fps(0),
    @@ -230,7 +231,6 @@ CGame::CGame(const std::string& mapname, const std::string& modName, ILoadSaveHa
     	gameOver(false),
     
     	noSpectatorChat(false),
    -	finishedLoading(false),
     
     	infoConsole(NULL),
     	consoleHistory(NULL),
    @@ -708,23 +708,20 @@ void CGame::ResizeEvent()
     }
     
     
    -int CGame::KeyPressed(unsigned short key, bool isRepeat)
    +int CGame::KeyPressed(unsigned short k, bool isRepeat)
     {
     	if (!gameOver && !isRepeat) {
     		playerHandler->Player(gu->myPlayerNum)->currentStats.keyPresses++;
     	}
     
    -	// Get the list of possible key actions
    -	const CKeySet ks(key, false);
    -	const CKeyBindings::ActionList& actionList = keyBindings->GetActionList(ks);
    -
     	if (!hotBinding.empty()) {
    -		if (key == SDLK_ESCAPE) {
    +		if (k == SDLK_ESCAPE) {
     			hotBinding.clear();
     		}
    -		else if (!keyCodes->IsModifier(key) && (key != keyBindings->GetFakeMetaKey())) {
    +		else if (!keyCodes->IsModifier(k) && (k != keyBindings->GetFakeMetaKey())) {
    +			CKeySet ks(k, false);
     			string cmd = "bind";
    -			cmd += " " + ks.GetString(false);
    +			cmd += " " + ks.GetString(false) ;
     			cmd += " " + hotBinding;
     			keyBindings->Command(cmd);
     			hotBinding.clear();
    @@ -733,15 +730,173 @@ int CGame::KeyPressed(unsigned short key, bool isRepeat)
     		return 0;
     	}
     
    +	// Get the list of possible key actions
    +	CKeySet ks(k, false);
    +	const CKeyBindings::ActionList& actionList = keyBindings->GetActionList(ks);
    +
     	if (userWriting) {
    -		for (unsigned int actionIndex = 0; actionIndex < actionList.size(); actionIndex++) {
    -			if (ProcessKeyPressAction(key, actionList[actionIndex])) {
    -				// the key was used, ignore it (ex: alt+a)
    -				ignoreNextChar = (actionIndex < (actionList.size() - 1));
    +		unsigned int actionIndex;
    +		for (actionIndex = 0; actionIndex < actionList.size(); actionIndex++) {
    +			const Action& action = actionList[actionIndex];
    +
    +			if (action.command == "edit_return") {
    +				userWriting=false;
    +				writingPos = 0;
    +
    +				if (k == SDLK_RETURN) {
    +					keyInput->SetKeyState(k, 0); //prevent game start when server chats
    +				}
    +				if (chatting) {
    +					string command;
    +					if ((userInput.find_first_of("aAsS") == 0) && (userInput[1] == ':')) {
    +						command = userInput.substr(2);
    +					} else {
    +						command = userInput;
    +					}
    +					if ((command[0] == '/') && (command[1] != '/')) {
    +						// execute an action
    +						consoleHistory->AddLine(command);
    +						const string actionLine = command.substr(1); // strip the '/'
    +						chatting = false;
    +						userInput = "";
    +						writingPos = 0;
    +						logOutput.Print(command);
    +						CKeySet ks(k, false);
    +						Action fakeAction(actionLine);
    +						ActionPressed(fakeAction, ks, isRepeat);
    +					}
    +				}
    +				break;
    +			}
    +			else if ((action.command == "edit_escape") &&
    +			         (chatting || inMapDrawer->wantLabel)) {
    +				if (chatting) {
    +					consoleHistory->AddLine(userInput);
    +				}
    +				userWriting=false;
    +				chatting=false;
    +				inMapDrawer->wantLabel=false;
    +				userInput="";
    +				writingPos = 0;
    +				break;
    +			}
    +			else if (action.command == "edit_complete") {
    +				string head = userInput.substr(0, writingPos);
    +				string tail = userInput.substr(writingPos);
    +				const vector<string> &partials = wordCompletion->Complete(head);
    +				userInput = head + tail;
    +				writingPos = (int)head.length();
    +				if (!partials.empty()) {
    +					string msg;
    +					for (unsigned int i = 0; i < partials.size(); i++) {
    +						msg += "  ";
    +						msg += partials[i];
    +					}
    +					logOutput.Print(msg);
    +				}
    +				break;
    +			}
    +			else if (action.command == "chatswitchall") {
    +				if ((userInput.find_first_of("aAsS") == 0) && (userInput[1] == ':')) {
    +					userInput = userInput.substr(2);
    +					writingPos = std::max(0, writingPos - 2);
    +				}
    +				userInputPrefix = "";
    +				break;
    +			}
    +			else if (action.command == "chatswitchally") {
    +				if ((userInput.find_first_of("aAsS") == 0) && (userInput[1] == ':')) {
    +					userInput[0] = 'a';
    +				} else {
    +					userInput = "a:" + userInput;
    +					writingPos += 2;
    +				}
    +				userInputPrefix = "a:";
    +				break;
    +			}
    +			else if (action.command == "chatswitchspec") {
    +				if ((userInput.find_first_of("aAsS") == 0) && (userInput[1] == ':')) {
    +					userInput[0] = 's';
    +				} else {
    +					userInput = "s:" + userInput;
    +					writingPos += 2;
    +				}
    +				userInputPrefix = "s:";
    +				break;
    +			}
    +			else if (action.command == "pastetext") {
    +				if (!action.extra.empty()) {
    +					userInput.insert(writingPos, action.extra);
    +					writingPos += action.extra.length();
    +				} else {
    +					PasteClipboard();
    +				}
    +				break;
    +			}
    +
    +			else if (action.command == "edit_backspace") {
    +				if (!userInput.empty() && (writingPos > 0)) {
    +					userInput.erase(writingPos - 1, 1);
    +					writingPos--;
    +				}
    +				break;
    +			}
    +			else if (action.command == "edit_delete") {
    +				if (!userInput.empty() && (writingPos < (int)userInput.size())) {
    +					userInput.erase(writingPos, 1);
    +				}
    +				break;
    +			}
    +			else if (action.command == "edit_home") {
    +				writingPos = 0;
    +				break;
    +			}
    +			else if (action.command == "edit_end") {
    +				writingPos = (int)userInput.length();
    +				break;
    +			}
    +			else if (action.command == "edit_prev_char") {
    +				writingPos = std::max(0, std::min((int)userInput.length(), writingPos - 1));
    +				break;
    +			}
    +			else if (action.command == "edit_next_char") {
    +				writingPos = std::max(0, std::min((int)userInput.length(), writingPos + 1));
    +				break;
    +			}
    +			else if (action.command == "edit_prev_word") {
    +				// prev word
    +				const char* s = userInput.c_str();
    +				int p = writingPos;
    +				while ((p > 0) && !isalnum(s[p - 1])) { p--; }
    +				while ((p > 0) &&  isalnum(s[p - 1])) { p--; }
    +				writingPos = p;
    +				break;
    +			}
    +			else if (action.command == "edit_next_word") {
    +				const int len = (int)userInput.length();
    +				const char* s = userInput.c_str();
    +				int p = writingPos;
    +				while ((p < len) && !isalnum(s[p])) { p++; }
    +				while ((p < len) &&  isalnum(s[p])) { p++; }
    +				writingPos = p;
    +				break;
    +			}
    +			else if ((action.command == "edit_prev_line") && chatting) {
    +				userInput = consoleHistory->PrevLine(userInput);
    +				writingPos = (int)userInput.length();
    +				break;
    +			}
    +			else if ((action.command == "edit_next_line") && chatting) {
    +				userInput = consoleHistory->NextLine(userInput);
    +				writingPos = (int)userInput.length();
     				break;
     			}
     		}
     
    +		if (actionIndex != actionList.size()) {
    +			ignoreNextChar = true; // the key was used, ignore it  (ex: alt+a)
    +		}
    +
     		return 0;
     	}
     
    @@ -749,15 +904,15 @@ int CGame::KeyPressed(unsigned short key, bool isRepeat)
     	std::deque<CInputReceiver*>& inputReceivers = GetInputReceivers();
     	std::deque<CInputReceiver*>::iterator ri;
     	for (ri = inputReceivers.begin(); ri != inputReceivers.end(); ++ri) {
    -		CInputReceiver* recv = *ri;
    -		if (recv && recv->KeyPressed(key, isRepeat)) {
    +		CInputReceiver* recv=*ri;
    +		if (recv && recv->KeyPressed(k, isRepeat)) {
     			return 0;
     		}
     	}
     
     	// try our list of actions
    -	for (unsigned int i = 0; i < actionList.size(); ++i) {
    -		if (ActionPressed(key, actionList[i], isRepeat)) {
    +	for (int i = 0; i < (int)actionList.size(); ++i) {
    +		if (ActionPressed(actionList[i], ks, isRepeat)) {
     			return 0;
     		}
     	}
    @@ -1609,12 +1764,9 @@ void CGame::SimFrame() {
     		m_validateAllAllocUnits();
     #endif
     
    -	// Important: gs->frameNum must be updated *before* GameFrame is called,
    -	// or any call-outs called by Lua will see a stale gs->frameNum.
    -	// (e.g. effective TTL of CEGs emitted in GameFrame will be reduced...)
    -	// It must still be passed the old frameNum because Lua may depend on it.
    -	// (e.g. initialization in frame 0...)
    -	eventHandler.GameFrame(gs->frameNum++);
    +	eventHandler.GameFrame(gs->frameNum);
    +
    +	gs->frameNum++;
     
     	if (!skipping) {
     		infoConsole->Update();
    diff --git a/rts/Game/Game.h b/rts/Game/Game.h
    index bfcb914..5837b90 100644
    --- a/rts/Game/Game.h
    +++ b/rts/Game/Game.h
    @@ -47,7 +47,6 @@ private:
     
     public:
     	CGame(const std::string& mapname, const std::string& modName, ILoadSaveHandler* saveFile);
    -	virtual ~CGame();
     
     	bool Draw();
     	bool DrawMT();
    @@ -57,22 +56,20 @@ public:
     	/// Called when a key is released by the user
     	int KeyReleased(unsigned short k);
     	/// Called when the key is pressed by the user (can be called several times due to key repeat)
    -	int KeyPressed(unsigned short k, bool isRepeat);
    +	int KeyPressed(unsigned short k,bool isRepeat);
     	void ResizeEvent();
    +	virtual ~CGame();
     
    -
    -	bool ProcessCommandText(unsigned int key, const std::string& command);
    -	bool ProcessKeyPressAction(unsigned int key, const Action& action);
    -
    -	bool ActionPressed(unsigned int key, const Action&, bool isRepeat);
    +	bool ActionPressed(const Action&, const CKeySet& ks, bool isRepeat);
     	bool ActionReleased(const Action&);
    -
    +	
     	bool HasLag() const;
     
    +	volatile bool finishedLoading;
    +
     	/// show GameEnd-window, calculate mouse movement etc.
     	void GameEnd(const std::vector<unsigned char>& winningAllyTeams);
     
    -
     	enum GameDrawMode {
     		gameNotDrawing     = 0,
     		gameNormalDraw     = 1,
    @@ -120,7 +117,6 @@ public:
     	bool showMTInfo;
     	/// Prevents spectator msgs from being seen by players
     	bool noSpectatorChat;
    -	volatile bool finishedLoading;
     
     	unsigned char gameID[16];
     
    @@ -142,7 +138,7 @@ private:
     	void SendNetChat(std::string message, int destination = -1);
     	/// Format and display a chat message received over network
     	void HandleChatMsg(const ChatMessage& msg);
    -
    +	
     	/// synced actions (received from server) go in here
     	void ActionReceived(const Action&, int playernum);
     
    diff --git a/rts/Game/GameHelper.cpp b/rts/Game/GameHelper.cpp
    index 404c9af..53b72de 100644
    --- a/rts/Game/GameHelper.cpp
    +++ b/rts/Game/GameHelper.cpp
    @@ -829,7 +829,7 @@ public:
     
     
     
    -void CGameHelper::GenerateWeaponTargets(const CWeapon* weapon, const CUnit* lastTargetUnit, std::multimap<float, CUnit*>& targets)
    +void CGameHelper::GenerateWeaponTargets(const CWeapon* weapon, CUnit* lastTarget, std::multimap<float, CUnit*>& targets)
     {
     	GML_RECMUTEX_LOCK(qnum); // GenerateTargets
     
    @@ -845,7 +845,7 @@ void CGameHelper::GenerateWeaponTargets(const CWeapon* weapon, const CUnit* last
     
     	const std::vector<int>& quads = qf->GetQuads(pos, radius + (aHeight - std::max(0.f, readmap->minheight)) * heightMod);
     
    -	const int tempNum = gs->tempNum++;
    +	int tempNum = gs->tempNum++;
     
     	typedef std::vector<int>::const_iterator VectorIt;
     	typedef std::list<CUnit*>::const_iterator ListIt;
    @@ -893,16 +893,13 @@ void CGameHelper::GenerateWeaponTargets(const CWeapon* weapon, const CUnit* last
     					const float modRange = radius + (aHeight - targPos.y) * heightMod;
     
     					if ((pos - targPos).SqLength2D() <= modRange * modRange) {
    -						const float dist2D = (pos - targPos).Length2D();
    -						const float rangeMul = (dist2D * weapon->weaponDef->proximityPriority + modRange * 0.4f + 100.0f);
    -						const float damageMul = weapon->weaponDef->damages[targetUnit->armorType] * targetUnit->curArmorMultiple;
    -
    -						targetPriority *= rangeMul;
    +						float dist2d = (pos - targPos).Length2D();
    +						targetPriority *= (dist2d * weapon->weaponDef->proximityPriority + modRange * 0.4f + 100.0f);
     
     						if (targetLOSState & LOS_INLOS) {
     							targetPriority *= (secDamage + targetUnit->health);
     
    -							if (targetUnit == lastTargetUnit) {
    +							if (targetUnit == lastTarget) {
     								targetPriority *= weapon->avoidTarget ? 10.0f : 0.4f;
     							}
     
    @@ -918,7 +915,9 @@ void CGameHelper::GenerateWeaponTargets(const CWeapon* weapon, const CUnit* last
     						}
     
     						if (targetLOSState & LOS_PREVLOS) {
    -							targetPriority /= (damageMul * targetUnit->power * (0.7f + gs->randFloat() * 0.6f));
    +							targetPriority /=
    +								weapon->weaponDef->damages[targetUnit->armorType] * targetUnit->curArmorMultiple *
    +								targetUnit->power * (0.7f + gs->randFloat() * 0.6f);
     
     							if (targetUnit->category & weapon->badTargetCategory) {
     								targetPriority *= 100.0f;
    @@ -945,6 +944,7 @@ void CGameHelper::GenerateWeaponTargets(const CWeapon* weapon, const CUnit* last
     		tracefile << "\n";
     	}
     #endif
    +
     }
     
     CUnit* CGameHelper::GetClosestUnit(const float3 &pos, float searchRadius)
    diff --git a/rts/Game/GameHelper.h b/rts/Game/GameHelper.h
    index b079d10..8d4845d 100644
    --- a/rts/Game/GameHelper.h
    +++ b/rts/Game/GameHelper.h
    @@ -55,7 +55,7 @@ public:
     	CUnit* GetClosestFriendlyUnit(const float3& pos, float searchRadius, int searchAllyteam);
     	CUnit* GetClosestEnemyAircraft(const float3& pos, float searchRadius, int searchAllyteam);
     
    -	void GenerateWeaponTargets(const CWeapon* attacker, const CUnit* lastTarget, std::multimap<float, CUnit*>& targets);
    +	void GenerateWeaponTargets(const CWeapon* attacker, CUnit* lastTarget, std::multimap<float, CUnit*>& targets);
     	float TraceRay(const float3& start, const float3& dir, float length, float power, const CUnit* owner, const CUnit*& hit, int collisionFlags = 0, const CFeature** hitFeature = NULL);
     	float GuiTraceRay(const float3& start, const float3& dir, float length, const CUnit*& hit, bool useRadar, const CUnit* exclude = NULL);
     	float GuiTraceRayFeature(const float3& start, const float3& dir, float length, const CFeature*& feature);
    diff --git a/rts/Game/PreGame.cpp b/rts/Game/PreGame.cpp
    index 69c4927..476d066 100644
    --- a/rts/Game/PreGame.cpp
    +++ b/rts/Game/PreGame.cpp
    @@ -214,23 +214,19 @@ void CPreGame::UpdateClientNet()
     					std::string message;
     					pckt >> message;
     					logOutput.Print(message);
    -					handleerror(NULL, "Remote requested quit: " + message, "Quit message", MBF_OK | MBF_EXCL);
    +					handleerror(NULL, message, "Quit message", MBF_OK | MBF_EXCL);
     				} catch (netcode::UnpackPacketException &e) {
     					logOutput.Print("Got invalid QuitMessage: %s", e.err.c_str());
     				}		
     				break;
     			}
    -			case NETMSG_CREATE_NEWPLAYER: {
    -				// server will send this first if we're using mid-game join
    -				// feature, to let us know about ourself (we won't be in
    -				// gamedata), otherwise skip to gamedata
    +			case NETMSG_CREATE_NEWPLAYER: { // server will send this first if we're using midgame join feature, to let us know about ourself (we won't be in gamedata), otherwise skip to gamedata
     				try {
     					netcode::UnpackPacket pckt(packet, 3);
     					unsigned char spectator, team, playerNum;
     					std::string name;
    -					// since the >> operator uses dest size to extract data from
    -					// the packet, we need to use temp variables of the same
    -					// size of the packet, before converting to dest variable
    +					// since the >> operator uses dest size to extract data from the packet, we need to use temp variables
    +					// of the same size of the packet, then convert to dest variable
     					pckt >> playerNum;
     					pckt >> spectator;
     					pckt >> team;
    @@ -241,30 +237,22 @@ void CPreGame::UpdateClientNet()
     					player.spectator = spectator;
     					player.team = team;
     					player.playerNum = playerNum;
    -					// add ourself, to avoid crashing if our player num gets
    -					// queried we will receive the same message later, in the
    -					// game class, which is the global broadcast version
    -					// the global broadcast will overwrite the user with the
    -					// same values as here
    +					// add ourself, to avoid crashing if our player num gets queried
    +					// we'll receive the same message later, in the game class, which is the global broadcast version
    +					// the global broadcast will overwrite the user with the same values as here
     					playerHandler->AddPlayer(player);
     				} catch (netcode::UnpackPacketException &e) {
     					logOutput.Print("Got invalid New player message: %s", e.err.c_str());
     				}
     				break;
     			}
    -			case NETMSG_GAMEDATA: {
    -				// server first sends this to let us know about teams, allyteams
    -				// etc.
    -				// (not if we are joining mid-game as extra players)
    -				// see NETMSG_SETPLAYERNUM
    +			case NETMSG_GAMEDATA: { // server first ( not if we're joining midgame as extra players ) sends this to let us know about teams, allyteams etc.
     				if (gameSetup)
     					throw content_error("Duplicate game data received from server");
     				GameDataReceived(packet);
     				break;
     			}
    -			case NETMSG_SETPLAYERNUM: {
    -				// this is sent after NETMSG_GAMEDATA, to let us know which
    -				// playernum we have
    +			case NETMSG_SETPLAYERNUM: { // this is sent afterwards to let us know which playernum we have
     				if (!gameSetup)
     					throw content_error("No game data received from server");
     
    diff --git a/rts/Game/UI/GuiHandler.cpp b/rts/Game/UI/GuiHandler.cpp
    index 0c298c2..87be01c 100644
    --- a/rts/Game/UI/GuiHandler.cpp
    +++ b/rts/Game/UI/GuiHandler.cpp
    @@ -1412,7 +1412,8 @@ void CGuiHandler::RunCustomCommands(const std::vector<std::string>& cmds, bool r
     
     				Action action(copy);
     				if (!ProcessLocalActions(action)) {
    -					game->ActionPressed(-1, action, false /*isRepeat*/);
    +					CKeySet ks;
    +					game->ActionPressed(action, ks, false /*isRepeat*/);
     				}
     
     				keyInput->SetKeyState(SDLK_LALT,   tmpAlt);
    diff --git a/rts/Game/UnsyncedGameCommands.cpp b/rts/Game/UnsyncedGameCommands.cpp
    index d7aae2a..ffb936b 100644
    --- a/rts/Game/UnsyncedGameCommands.cpp
    +++ b/rts/Game/UnsyncedGameCommands.cpp
    @@ -15,7 +15,7 @@
     #include "PlayerRoster.h"
     #include "TimeProfiler.h"
     #include "IVideoCapturing.h"
    -#include "WordCompletion.h"
    +#include "Game/UI/UnitTracker.h"
     #ifdef _WIN32
     #  include "winerror.h"
     #endif
    @@ -55,7 +55,6 @@
     #include "UI/SelectionKeyHandler.h"
     #include "UI/ShareBox.h"
     #include "UI/TooltipConsole.h"
    -#include "UI/UnitTracker.h"
     #include "UI/ProfileDrawer.h"
     #include "System/ConfigHandler.h"
     #include "System/EventHandler.h"
    @@ -89,186 +88,9 @@ static std::vector<std::string> _local_strSpaceTokenize(const std::string& text)
     }
     
     
    -
    -bool CGame::ProcessCommandText(unsigned int key, const std::string& command) {
    -	if (command.size() <= 2)
    -		return false;
    -
    -	if ((command[0] == '/') && (command[1] != '/')) {
    -		const string actionLine = command.substr(1); // strip the '/'
    -
    -		Action fakeAction(actionLine);
    -		ActionPressed(key, fakeAction, false);
    -		return true;
    -	}
    -
    -	return false;
    -}
    -
    -bool CGame::ProcessKeyPressAction(unsigned int key, const Action& action) {
    -	if (action.command == "edit_return") {
    -		userWriting = false;
    -		writingPos = 0;
    -
    -		if (key == SDLK_RETURN) {
    -			// prevent game start when host enters a chat message
    -			keyInput->SetKeyState(key, 0);
    -		}
    -		if (chatting) {
    -			string command;
    -
    -			if ((userInput.find_first_of("aAsS") == 0) && (userInput[1] == ':')) {
    -				command = userInput.substr(2);
    -			} else {
    -				command = userInput;
    -			}
    -
    -			if (ProcessCommandText(key, command)) {
    -				// execute an action
    -				consoleHistory->AddLine(command);
    -				logOutput.Print(command);
    -
    -				chatting = false;
    -				userInput = "";
    -				writingPos = 0;
    -			}
    -		}
    -		return true;
    -	}
    -	else if ((action.command == "edit_escape") && (chatting || inMapDrawer->wantLabel)) {
    -		if (chatting) {
    -			consoleHistory->AddLine(userInput);
    -		}
    -		userWriting = false;
    -		chatting = false;
    -		inMapDrawer->wantLabel = false;
    -		userInput = "";
    -		writingPos = 0;
    -		return true;
    -	}
    -	else if (action.command == "edit_complete") {
    -		string head = userInput.substr(0, writingPos);
    -		string tail = userInput.substr(writingPos);
    -		const vector<string> &partials = wordCompletion->Complete(head);
    -		userInput = head + tail;
    -		writingPos = (int)head.length();
    -
    -		if (!partials.empty()) {
    -			string msg;
    -			for (unsigned int i = 0; i < partials.size(); i++) {
    -				msg += "  ";
    -				msg += partials[i];
    -			}
    -			logOutput.Print(msg);
    -		}
    -		return true;
    -	}
    -	else if (action.command == "chatswitchall") {
    -		if ((userInput.find_first_of("aAsS") == 0) && (userInput[1] == ':')) {
    -			userInput = userInput.substr(2);
    -			writingPos = std::max(0, writingPos - 2);
    -		}
    -
    -		userInputPrefix = "";
    -		return true;
    -	}
    -	else if (action.command == "chatswitchally") {
    -		if ((userInput.find_first_of("aAsS") == 0) && (userInput[1] == ':')) {
    -			userInput[0] = 'a';
    -		} else {
    -			userInput = "a:" + userInput;
    -			writingPos += 2;
    -		}
    -
    -		userInputPrefix = "a:";
    -		return true;
    -	}
    -	else if (action.command == "chatswitchspec") {
    -		if ((userInput.find_first_of("aAsS") == 0) && (userInput[1] == ':')) {
    -			userInput[0] = 's';
    -		} else {
    -			userInput = "s:" + userInput;
    -			writingPos += 2;
    -		}
    -
    -		userInputPrefix = "s:";
    -		return true;
    -	}
    -	else if (action.command == "pastetext") {
    -		if (!action.extra.empty()) {
    -			userInput.insert(writingPos, action.extra);
    -			writingPos += action.extra.length();
    -		} else {
    -			PasteClipboard();
    -		}
    -		return true;
    -	}
    -
    -	else if (action.command == "edit_backspace") {
    -		if (!userInput.empty() && (writingPos > 0)) {
    -			userInput.erase(writingPos - 1, 1);
    -			writingPos--;
    -		}
    -		return true;
    -	}
    -	else if (action.command == "edit_delete") {
    -		if (!userInput.empty() && (writingPos < (int)userInput.size())) {
    -			userInput.erase(writingPos, 1);
    -		}
    -		return true;
    -	}
    -	else if (action.command == "edit_home") {
    -		writingPos = 0;
    -		return true;
    -	}
    -	else if (action.command == "edit_end") {
    -		writingPos = (int)userInput.length();
    -		return true;
    -	}
    -	else if (action.command == "edit_prev_char") {
    -		writingPos = std::max(0, std::min((int)userInput.length(), writingPos - 1));
    -		return true;
    -	}
    -	else if (action.command == "edit_next_char") {
    -		writingPos = std::max(0, std::min((int)userInput.length(), writingPos + 1));
    -		return true;
    -	}
    -	else if (action.command == "edit_prev_word") {
    -		// prev word
    -		const char* s = userInput.c_str();
    -		int p = writingPos;
    -		while ((p > 0) && !isalnum(s[p - 1])) { p--; }
    -		while ((p > 0) &&  isalnum(s[p - 1])) { p--; }
    -		writingPos = p;
    -		return true;
    -	}
    -	else if (action.command == "edit_next_word") {
    -		const int len = (int)userInput.length();
    -		const char* s = userInput.c_str();
    -		int p = writingPos;
    -		while ((p < len) && !isalnum(s[p])) { p++; }
    -		while ((p < len) &&  isalnum(s[p])) { p++; }
    -		writingPos = p;
    -		return true;
    -	}
    -	else if ((action.command == "edit_prev_line") && chatting) {
    -		userInput = consoleHistory->PrevLine(userInput);
    -		writingPos = (int)userInput.length();
    -		return true;
    -	}
    -	else if ((action.command == "edit_next_line") && chatting) {
    -		userInput = consoleHistory->NextLine(userInput);
    -		writingPos = (int)userInput.length();
    -		return true;
    -	}
    -
    -	return false;
    -}
    -
    -
    -
     // FOR UNSYNCED MESSAGES
    -bool CGame::ActionPressed(unsigned int key, const Action& action, bool isRepeat)
    +bool CGame::ActionPressed(const Action& action,
    +                          const CKeySet& ks, bool isRepeat)
     {
     	// we may need these later
     	CBaseGroundDrawer* gd = readmap->GetGroundDrawer();
    @@ -409,7 +231,7 @@ bool CGame::ActionPressed(unsigned int key, const Action& action, bool isRepeat)
     		if (pos.x >= 0) {
     			inMapDrawer->keyPressed = false;
     			inMapDrawer->PromptLabel(pos);
    -			if ((key >= SDLK_SPACE) && (key <= SDLK_DELETE)) {
    +			if ((ks.Key() >= SDLK_SPACE) && (ks.Key() <= SDLK_DELETE)) {
     				ignoreNextChar=true;
     			}
     		}
    @@ -803,7 +625,7 @@ bool CGame::ActionPressed(unsigned int key, const Action& action, bool isRepeat)
     	else if (((cmd == "chat")     || (cmd == "chatall") ||
     	         (cmd == "chatally") || (cmd == "chatspec")) &&
     	         // if chat is bound to enter and we're waiting for user to press enter to start game, ignore.
    -				  (key != SDLK_RETURN || playing || !keyInput->IsKeyPressed(SDLK_LCTRL))) {
    +				  (ks.Key() != SDLK_RETURN || playing || !keyInput->IsKeyPressed(SDLK_LCTRL))) {
     
     		if (cmd == "chatall")  { userInputPrefix = ""; }
     		if (cmd == "chatally") { userInputPrefix = "a:"; }
    @@ -814,7 +636,7 @@ bool CGame::ActionPressed(unsigned int key, const Action& action, bool isRepeat)
     		writingPos = (int)userInput.length();
     		chatting = true;
     
    -		if (key != SDLK_RETURN) {
    +		if (ks.Key() != SDLK_RETURN) {
     			ignoreNextChar = true;
     		}
     
    @@ -1646,3 +1468,5 @@ bool CGame::ActionPressed(unsigned int key, const Action& action, bool isRepeat)
     
     	return true;
     }
    +
    +
    diff --git a/rts/Rendering/Env/AdvSky.cpp b/rts/Rendering/Env/AdvSky.cpp
    index 08393fe..680fa0f 100644
    --- a/rts/Rendering/Env/AdvSky.cpp
    +++ b/rts/Rendering/Env/AdvSky.cpp
    @@ -46,7 +46,6 @@ CAdvSky::CAdvSky()
     	domeWidth = sin(2*PI/32)*400*1.7f;
     
     	UpdateSkyDir();
    -	InitSun();
     	UpdateSunDir();
     
     	for(int a=0;a<CLOUD_DETAIL;a++)
    @@ -65,15 +64,11 @@ CAdvSky::CAdvSky()
     	CreateClouds();
     	dynamicSky = configHandler->Get("DynamicSky", false);
     
    +	InitSun();
     	oldCoverBaseX=-5;
     
     	cloudFP = LoadFragmentProgram("ARB/clouds.fp");
     
    -	CreateSkyDomeList();
    -}
    -
    -void CAdvSky::CreateSkyDomeList()
    -{
     	glGetError();
     	skyDomeList = glGenLists(1);
     	glNewList(skyDomeList, GL_COMPILE);
    @@ -665,7 +660,7 @@ void CAdvSky::InitSun()
     	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
     	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
     //	gluBuild2DMipmaps(GL_TEXTURE_2D,1 ,32, 2, GL_ALPHA, GL_UNSIGNED_BYTE, mem);
    -	glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE ,32, 4, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, mem);
    +	glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE ,32, 4,0, GL_LUMINANCE, GL_UNSIGNED_BYTE, mem);
     
     	delete [] mem;
     }
    diff --git a/rts/Rendering/Env/AdvSky.h b/rts/Rendering/Env/AdvSky.h
    index f963437..5686ce9 100644
    --- a/rts/Rendering/Env/AdvSky.h
    +++ b/rts/Rendering/Env/AdvSky.h
    @@ -21,7 +21,6 @@ public:
     	void UpdateSkyTexture();
     
     private:
    -	void CreateSkyDomeList();
     	void InitSun();
     	void UpdateSunFlare();
     	void CreateCover(int baseX, int baseY, float* buf);
    diff --git a/rts/Rendering/Env/BaseTreeDrawer.cpp b/rts/Rendering/Env/BaseTreeDrawer.cpp
    index bcfcd20..31ebe22 100644
    --- a/rts/Rendering/Env/BaseTreeDrawer.cpp
    +++ b/rts/Rendering/Env/BaseTreeDrawer.cpp
    @@ -7,7 +7,6 @@
     #include "BasicTreeDrawer.h"
     #include "AdvTreeDrawer.h"
     #include "Game/Camera.h"
    -#include "Rendering/GlobalRendering.h"
     #include "Sim/Features/FeatureHandler.h"
     #include "Sim/Features/Feature.h"
     #include "Sim/Misc/GlobalConstants.h"
    @@ -83,8 +82,7 @@ void CBaseTreeDrawer::DrawShadowPass()
     
     void CBaseTreeDrawer::Draw(bool drawReflection)
     {
    -	const float maxDistance = CGlobalRendering::MAX_VIEW_RANGE / (SQUARE_SIZE * TREE_SQUARE_SIZE);
    -	const float treeDistance = Clamp(baseTreeDistance, 1.0f, maxDistance);
    +	const float treeDistance = Clamp(baseTreeDistance, 1.0f, (float)MAX_VIEW_RANGE / (SQUARE_SIZE * TREE_SQUARE_SIZE));
     	Draw(treeDistance, drawReflection);
     }
     
    diff --git a/rts/Rendering/Env/BasicSky.cpp b/rts/Rendering/Env/BasicSky.cpp
    index 67621f5..10b2b3e 100644
    --- a/rts/Rendering/Env/BasicSky.cpp
    +++ b/rts/Rendering/Env/BasicSky.cpp
    @@ -48,7 +48,6 @@ CBasicSky::CBasicSky()
     	domeWidth=sin(PI/16)*400*1.7f;
     
     	UpdateSkyDir();
    -	InitSun();
     	UpdateSunDir();
     
     	cloudDensity = 0.25f + mapInfo->atmosphere.cloudDensity * 0.5f;
    @@ -67,9 +66,8 @@ CBasicSky::CBasicSky()
     	CreateClouds();
     	dynamicSky = configHandler->Get("DynamicSky", false);
     
    +	InitSun();
     	oldCoverBaseX=-5;
    -
    -	CreateSkyDomeList();
     }
     
     void CBasicSky::CreateSkyDomeList()
    @@ -514,7 +512,7 @@ void CBasicSky::DrawSun()
     
     	glDisable(GL_DEPTH_TEST);
     	glDisable(GL_ALPHA_TEST);
    -	static unsigned char buf[32];
    +	static unsigned char buf[128];
     	glEnable(GL_TEXTURE_2D);
     
     	float3 modCamera=sundir1*camera->pos.x+sundir2*camera->pos.z;
    @@ -538,6 +536,7 @@ void CBasicSky::DrawSun()
     	}
     
     	float mid=0;
    +	unsigned char *bf=buf+32, *bf2=buf+64;
     	for(int x=0;x<32;++x){
     		float cx1=(*cvs++)*(1-fx)+(*cvs1++)*fx;
     		float cx2=(*cvs2++)*(1-fx)+(*cvs3++)*fx;
    @@ -546,6 +545,9 @@ void CBasicSky::DrawSun()
     		if(cover>127.5f)
     			cover=127.5f;
     		mid+=cover;
    +
    +		(*bf++)=(unsigned char)(255-cover*2);
    +		(*bf2++)=(unsigned char)(128-cover);
     	}
     	mid*=1.0f/32;
     	for(int x=0;x<32;++x){
    @@ -553,7 +555,7 @@ void CBasicSky::DrawSun()
     	}
     
     	glBindTexture(GL_TEXTURE_2D, sunFlareTex);
    -	glTexSubImage2D(GL_TEXTURE_2D,0,0,0,32,1,GL_LUMINANCE,GL_UNSIGNED_BYTE,buf);
    +	glTexSubImage2D(GL_TEXTURE_2D,0,0,0,32,3,GL_LUMINANCE,GL_UNSIGNED_BYTE,buf);
     
     	const float si = skyLight->GetLightIntensity();
     	const float3 sc = sunColor * si;
    @@ -584,9 +586,9 @@ void CBasicSky::UpdateSunFlare() {
     			float dx=sin(x*2*PI/256.0f);
     			float dy=cos(x*2*PI/256.0f);
     
    -			glTexCoord2f(x/256.0f,0.25f);
    +			glTexCoord2f(x/256.0f,0.125f);
     			glVertexf3(modSunDir*5+ldir*dx*0.0014f+udir*dy*0.0014f);
    -			glTexCoord2f(x/256.0f,0.75f);
    +			glTexCoord2f(x/256.0f,0.875f);
     			glVertexf3(modSunDir*5+ldir*dx*4+udir*dy*4);
     		}
     		glEnd();
    @@ -621,7 +623,7 @@ void CBasicSky::InitSun()
     	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
     	glBuildMipmaps(GL_TEXTURE_2D,GL_RGBA8 ,128, 128, GL_RGBA, GL_UNSIGNED_BYTE, mem);
     
    -	for(int y=0;y<2;++y){
    +	for(int y=0;y<4;++y){
     		for(int x=0;x<32;++x){
     			if(y==0 && x%2)
     				mem[(y*32+x)]=255;
    @@ -635,7 +637,7 @@ void CBasicSky::InitSun()
     	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
     	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
     	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
    -	glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE ,32, 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, mem);
    +	glTexImage2D(GL_TEXTURE_2D,0, GL_LUMINANCE, 32, 4, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, mem);
     
     	delete[] mem;
     }
    @@ -793,13 +795,14 @@ void CBasicSky::UpdateTexPartDot3(int x, int y, unsigned char (*texp)[4]) {
     
     	const float sunInt = skyLight->GetLightIntensity();
     	const float sunDist = acos(dir.dot(skyLight->GetLightDir())) * 50;
    -	const float sunMod = sunInt * (0.3f / math::sqrt(sunDist) + 2.0f / sunDist);
    +	const float sunMod = sunInt * (0.3f / math::sqrt(sunDist) + 3.0f / (1 + sunDist));
     
     	const float green = std::min(1.0f, (0.55f + sunMod));
    +	const float blue  = 203 - sunInt * (40.0f / (3 + sunDist));
     
     	texp[x][0] = (unsigned char) (sunInt * (255 - std::min(255.0f, sunDist))); // sun on borders
     	texp[x][1] = (unsigned char) (green * 255); // sun light through
    -	texp[x][2] = (unsigned char)  203; // ambient
    +	texp[x][2] = (unsigned char)  blue; // ambient
     	texp[x][3] = 255;
     }
     
    diff --git a/rts/Rendering/FarTextureHandler.cpp b/rts/Rendering/FarTextureHandler.cpp
    old mode 100644
    new mode 100755
    index 3a7978a..74f20f4
    --- a/rts/Rendering/FarTextureHandler.cpp
    +++ b/rts/Rendering/FarTextureHandler.cpp
    @@ -181,7 +181,7 @@ void CFarTextureHandler::CreateFarTexture(const CSolidObject* obj)
     	unitDrawer->SetupForUnitDrawing();
     	unitDrawer->GetOpaqueModelRenderer(model->type)->PushRenderState();
     
    -	if (model->type == MODELTYPE_S3O || model->type == MODELTYPE_OBJ) {
    +	if (model->type == MODELTYPE_S3O || model->type == MODELTYPE_OBJ || model->type == MODELTYPE_ASS) {
     		// FIXME for some strange reason we need to invert the culling, why?
     		if (model->type == MODELTYPE_S3O) {
     			glCullFace(GL_FRONT);
    diff --git a/rts/Rendering/FeatureDrawer.cpp b/rts/Rendering/FeatureDrawer.cpp
    old mode 100644
    new mode 100755
    index 4385a71..b88eab3
    --- a/rts/Rendering/FeatureDrawer.cpp
    +++ b/rts/Rendering/FeatureDrawer.cpp
    @@ -229,7 +229,7 @@ void CFeatureDrawer::DrawOpaqueFeatures(int modelType)
     	FeatureSet::iterator featureSetIt;
     
     	for (featureBinIt = featureBin.begin(); featureBinIt != featureBin.end(); ++featureBinIt) {
    -		if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ) {
    +		if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ || modelType == MODELTYPE_ASS) {
     			texturehandlerS3O->SetS3oTexture(featureBinIt->first);
     		}
     
    @@ -371,7 +371,7 @@ void CFeatureDrawer::DrawFadeFeaturesHelper(int modelType) {
     		FeatureRenderBin& featureBin = cloakedModelRenderers[modelType]->GetFeatureBinMutable();
     
     		for (FeatureRenderBinIt it = featureBin.begin(); it != featureBin.end(); ++it) {
    -			if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ) {
    +			if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ || modelType == MODELTYPE_ASS) {
     				texturehandlerS3O->SetS3oTexture(it->first);
     			}
     
    diff --git a/rts/Rendering/GlobalRendering.cpp b/rts/Rendering/GlobalRendering.cpp
    index 60bd337..86d328b 100644
    --- a/rts/Rendering/GlobalRendering.cpp
    +++ b/rts/Rendering/GlobalRendering.cpp
    @@ -22,8 +22,6 @@
      */
     CGlobalRendering* globalRendering;
     
    -const float CGlobalRendering::MAX_VIEW_RANGE = 8000.0f;
    -const float CGlobalRendering::NEAR_PLANE = 2.8f;
     
     CR_BIND(CGlobalRendering, );
     
    diff --git a/rts/Rendering/GlobalRendering.h b/rts/Rendering/GlobalRendering.h
    index 38525b0..e60ba46 100644
    --- a/rts/Rendering/GlobalRendering.h
    +++ b/rts/Rendering/GlobalRendering.h
    @@ -4,6 +4,8 @@
     #define _GLOBAL_RENDERING_H
     
     #include "System/creg/creg_cond.h"
    +#include "System/float4.h"
    +#include "System/Matrix44f.h"
     
     /**
      * @brief Globally accessible unsynced, rendering related data
    @@ -180,18 +182,6 @@ public:
     	 * @brief full-screen or windowed rendering
     	 */
     	bool fullScreen;
    -
    -
    -
    -	/**
    -	* @brief max view range in elmos
    -	*/
    -	static const float MAX_VIEW_RANGE;
    -
    -	/**
    -	* @brief near z-plane distance in elmos
    -	*/
    -	static const float NEAR_PLANE;
     };
     
     extern CGlobalRendering* globalRendering;
    diff --git a/rts/Rendering/Models/3DModel.cpp b/rts/Rendering/Models/3DModel.cpp
    old mode 100644
    new mode 100755
    index 5c3d98d..02df7b7
    --- a/rts/Rendering/Models/3DModel.cpp
    +++ b/rts/Rendering/Models/3DModel.cpp
    @@ -18,6 +18,18 @@
     
     
     //////////////////////////////////////////////////////////////////////
    +// S3DModel
    +//
    +
    +S3DModelPiece* S3DModel::FindPiece( std::string name )
    +{
    +    const ModelPieceMap::const_iterator it = pieces.find(name);
    +    if (it != pieces.end()) return it->second;
    +    return NULL;
    +}
    +
    +
    +//////////////////////////////////////////////////////////////////////
     // S3DModelPiece
     //
     
    diff --git a/rts/Rendering/Models/3DModel.h b/rts/Rendering/Models/3DModel.h
    old mode 100644
    new mode 100755
    index ba577b7..0501a41
    --- a/rts/Rendering/Models/3DModel.h
    +++ b/rts/Rendering/Models/3DModel.h
    @@ -5,23 +5,26 @@
     
     #include <vector>
     #include <string>
    -
    +#include <set>
    +#include <map>
     #include "System/Matrix44f.h"
     
     
    -
     const int
     	MODELTYPE_3DO   = 0,
     	MODELTYPE_S3O   = 1,
     	MODELTYPE_OBJ   = 2,
    -	MODELTYPE_OTHER = 3;
    +	MODELTYPE_ASS	= 3, // Model loaded by Assimp library
    +	MODELTYPE_OTHER	= 4; // For future use. Still used in some parts of code.
     
     struct CollisionVolume;
     struct S3DModel;
     struct S3DModelPiece;
     struct LocalModel;
     struct LocalModelPiece;
    +struct aiScene;
     
    +typedef std::map<std::string, S3DModelPiece*> ModelPieceMap;
     
     struct S3DModelPiece {
     	S3DModelPiece(): type(-1) {
    @@ -53,8 +56,10 @@ struct S3DModelPiece {
     
     
     	std::string name;
    +	std::string parentName;
     	std::vector<S3DModelPiece*> childs;
     
    +    S3DModel* model;
     	S3DModelPiece* parent;
     	CollisionVolume* colvol;
     
    @@ -68,23 +73,32 @@ struct S3DModelPiece {
     	float3 maxs;
     	float3 offset;    // wrt. parent
     	float3 goffset;   // wrt. root
    +	float3 rot;
    +	float3 scale;
     };
     
     
     struct S3DModel
     {
     	S3DModel(): id(-1), type(-1), textureType(-1) {
    -		numPieces = 0;
    -
    -		radius = 0.0f;
    -		height = 0.0f;
    -
    -		rootPiece = NULL;
    +        height = 0.0f;
    +        radius = 0.0f;
    +        relMidPos = float3(0.0f, 0.0f, 0.0f);
    +        mins = float3(10000.0f, 10000.0f, 10000.0f);
    +        maxs = float3(-10000.0f, -10000.0f, -10000.0f);
    +        tex1 = "default.png";
    +        tex2 = "";
    +        flipTexY = false;
    +        invertTexAlpha = false;
    +        numPieces = 0;
    +        rootPiece = NULL;
    +        scene = NULL;
     	}
     
     	S3DModelPiece* GetRootPiece() { return rootPiece; }
     	void SetRootPiece(S3DModelPiece* p) { rootPiece = p; }
     	void DrawStatic() const { rootPiece->DrawStatic(); }
    +	S3DModelPiece* FindPiece( std::string name );
     
     	std::string name;
     	std::string tex1;
    @@ -93,8 +107,9 @@ struct S3DModel
     	int id;                 //! unsynced ID, starting with 1
     	int type;               //! MODELTYPE_*
     	int textureType;        //! FIXME: MAKE S3O ONLY (0 = 3DO, otherwise S3O or OBJ)
    +	bool flipTexY;			//! Turn both textures upside down before use
    +	bool invertTexAlpha;	//! Invert teamcolor alpha channel in S3O texture 1
     
    -	int numPieces;
     	float radius;
     	float height;
     
    @@ -102,7 +117,10 @@ struct S3DModel
     	float3 maxs;
     	float3 relMidPos;
     
    -	S3DModelPiece* rootPiece;
    +	int numPieces;
    +    S3DModelPiece* rootPiece;   //! The piece at the base of the model hierarchy
    +    ModelPieceMap pieces;       //! Lookup table for pieces by name
    +    const aiScene* scene;       //! Assimp scene containing all loaded model data. NULL for S30/3DO.
     };
     
     
    diff --git a/rts/Rendering/Models/3DModelLog.h b/rts/Rendering/Models/3DModelLog.h
    new file mode 100644
    index 0000000..c1b38e2
    --- /dev/null
    +++ b/rts/Rendering/Models/3DModelLog.h
    @@ -0,0 +1,14 @@
    +/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
    +
    +#ifndef _3DMODELLOG_H_
    +#define _3DMODELLOG_H_
    +
    +#include "System/LogOutput.h"
    +
    +// Enabling the "Model" and "Piece" log subsystems will help debug model loading. "Model-detail" is extremely verbose.
    +static CLogSubsystem LOG_MODEL("Model");
    +static CLogSubsystem LOG_MODEL_DETAIL("Model-detail");
    +static CLogSubsystem LOG_PIECE("Piece");
    +static CLogSubsystem LOG_PIECE_DETAIL("Piece-detail");
    +
    +#endif // _3DMODELLOG_H_
    diff --git a/rts/Rendering/Models/AssIO.cpp b/rts/Rendering/Models/AssIO.cpp
    new file mode 100644
    index 0000000..9a2a1cb
    --- /dev/null
    +++ b/rts/Rendering/Models/AssIO.cpp
    @@ -0,0 +1,96 @@
    +#include "StdAfx.h"
    +
    +#include <string>
    +
    +#include "AssIO.h"
    +
    +#include "FileSystem/FileHandler.h"
    +
    +AssVFSStream::AssVFSStream(const std::string& pFile, const std::string& pMode)
    +{
    +	file = new CFileHandler(pFile, pMode);
    +}
    +
    +AssVFSStream::~AssVFSStream(void)
    +{
    +	delete file;
    +}
    +
    +size_t AssVFSStream::Read( void* pvBuffer, size_t pSize, size_t pCount)
    +{
    +	// Spring VFS only supports reading chars. Need to convert.
    +	int length = pSize * pCount;
    +	return file->Read(pvBuffer, length);
    +}
    +
    +size_t AssVFSStream::Write( const void* pvBuffer, size_t pSize, size_t pCount)
    +{
    +	// FAKE. Shouldn't need to write back to VFS
    +	return pSize * pCount;
    +}
    +
    +aiReturn AssVFSStream::Seek( size_t pOffset, aiOrigin pOrigin)
    +{
    +	switch(pOrigin){
    +		case aiOrigin_SET: // from start of file
    +			if ( pOffset >= file->FileSize() ) return AI_FAILURE;
    +			file->Seek( pOffset, std::ios_base::beg );
    +			break;
    +		case aiOrigin_CUR: // from current position in file
    +			if ( pOffset >= ( file->FileSize() + file->GetPos() ) ) return AI_FAILURE;
    +			file->Seek( pOffset, std::ios_base::cur );
    +			break;
    +		case aiOrigin_END: // reverse from end of file
    +			if ( pOffset >= file->FileSize() ) return AI_FAILURE;
    +			file->Seek( pOffset, std::ios_base::end );
    +			break;
    +	}
    +	return AI_SUCCESS;
    +}
    +
    +size_t AssVFSStream::Tell() const
    +{
    +	return file->GetPos();
    +}
    +
    +
    +size_t AssVFSStream::FileSize() const
    +{
    +	int filesize = file->FileSize();
    +	if ( filesize < 0 ) filesize = 0;
    +	return filesize;
    +}
    +
    +void AssVFSStream::Flush () // FAKE
    +{
    +}
    +
    +
    +// Check whether a specific file exists
    +bool AssVFSSystem::Exists( const char* pFile) const
    +{
    +	CFileHandler file(pFile);
    +	return file.FileExists();
    +}
    +
    +// Get the path delimiter character we'd like to get
    +char AssVFSSystem::getOsSeparator() const
    +{
    +	return '/';
    +}
    +
    +bool AssVFSSystem::ComparePaths (const std::string& one, const std::string& second) const
    +{
    +	return one == second; // too naive? probably should convert to absolute paths
    +}
    +
    +// open a custom stream
    +Assimp::IOStream* AssVFSSystem::Open( const char* pFile, const char* pMode)
    +{
    +	return new AssVFSStream( pFile, pMode );
    +}
    +
    +void AssVFSSystem::Close( Assimp::IOStream* pFile)
    +{
    +	delete pFile;
    +}
    diff --git a/rts/Rendering/Models/AssIO.h b/rts/Rendering/Models/AssIO.h
    new file mode 100644
    index 0000000..7b34efa
    --- /dev/null
    +++ b/rts/Rendering/Models/AssIO.h
    @@ -0,0 +1,52 @@
    +#ifndef ASSIO_H
    +#define ASSIO_H
    +
    +#include "IOStream.h"
    +#include "IOSystem.h"
    +class CFileHandler;
    +
    +// Custom implementation of Assimp IOStream to support Spring's VFS
    +// Required because Assimp models often need to load textures from other files
    +
    +class AssVFSStream : public Assimp::IOStream
    +{
    +	friend class AssVFSSystem;
    +	CFileHandler* file;
    +
    +protected:
    +	// Constructor protected for private usage by AssVFSSystem
    +	AssVFSStream(const std::string& pFile, const std::string& pMode);
    +
    +public:
    +	~AssVFSStream(void);
    +	size_t Read( void* pvBuffer, size_t pSize, size_t pCount);
    +	size_t Write( const void* pvBuffer, size_t pSize, size_t pCount);
    +	aiReturn Seek( size_t pOffset, aiOrigin pOrigin);
    +	size_t Tell() const;
    +	size_t FileSize() const;
    +	void Flush ();
    +};
    +
    +
    +// Spring VFS Filesystem Wrapper for Assimp
    +
    +class AssVFSSystem : public Assimp::IOSystem
    +{
    +public:
    +	AssVFSSystem() { }
    +	~AssVFSSystem() { }
    +
    +	// Check whether a specific file exists
    +	bool Exists( const char* pFile) const;
    +
    +	// Get the path delimiter character we'd like to get
    +	char getOsSeparator() const;
    +	bool ComparePaths (const std::string& one, const std::string& second) const;
    +
    +	// open a custom stream
    +	Assimp::IOStream* Open( const char* pFile, const char* pMode  = "rb" );
    +
    +	void Close( Assimp::IOStream* pFile);
    +};
    +
    +#endif
    diff --git a/rts/Rendering/Models/AssParser.cpp b/rts/Rendering/Models/AssParser.cpp
    new file mode 100755
    index 0000000..2678a01
    --- /dev/null
    +++ b/rts/Rendering/Models/AssParser.cpp
    @@ -0,0 +1,565 @@
    +// AssParser.cpp: implementation of the CAssParser class. Reads 3D formats supported by Assimp lib.
    +//
    +//////////////////////////////////////////////////////////////////////
    +//#include "StdAfx.h"
    +
    +#include "Util.h"
    +#include "LogOutput.h"
    +#include "Platform/errorhandler.h"
    +#include "System/Exceptions.h"
    +#include "Sim/Misc/CollisionVolume.h"
    +#include "FileSystem/FileHandler.h"
    +#include "Lua/LuaParser.h"
    +#include "3DModel.h"
    +#include "3DModelLog.h"
    +#include "S3OParser.h"
    +#include "AssIO.h"
    +#include "AssParser.h"
    +
    +#include "assimp.hpp"
    +#include "aiDefines.h"
    +#include "aiTypes.h"
    +#include "aiScene.h"
    +#include "aiPostProcess.h"
    +#include "DefaultLogger.h"
    +#include "Rendering/Textures/S3OTextureHandler.h"
    +
    +#define IS_QNAN(f) (f != f)
    +#define DEGTORAD 0.0174532925
    +
    +// triangulate guarantees the most complex mesh is a triangle
    +// sortbytype ensure only 1 type of primitive type per mesh is used
    +#define ASS_POSTPROCESS_OPTIONS \
    +    aiProcess_RemoveComponent               | \
    +	aiProcess_FindInvalidData				| \
    +	aiProcess_CalcTangentSpace				| \
    +	aiProcess_GenSmoothNormals				| \
    +	aiProcess_SplitLargeMeshes				| \
    +	aiProcess_Triangulate					| \
    +	aiProcess_GenUVCoords             		| \
    +	aiProcess_SortByPType					| \
    +	aiProcess_JoinIdenticalVertices
    +
    +//aiProcess_ImproveCacheLocality
    +
    +// Convert Assimp quaternion to radians around x, y and z
    +float3 QuaternionToRadianAngles( aiQuaternion q1 )
    +{
    +    float sqw = q1.w*q1.w;
    +    float sqx = q1.x*q1.x;
    +    float sqy = q1.y*q1.y;
    +    float sqz = q1.z*q1.z;
    +	float unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor
    +	float test = q1.x*q1.y + q1.z*q1.w;
    +
    +	float3 result(0.0f, 0.0f, 0.0f);
    +
    +	if (test > 0.499f * unit) { // singularity at north pole
    +		result.x = 2 * atan2(q1.x,q1.w);
    +		result.y = PI/2;
    +	} else if (test < -0.499f * unit) { // singularity at south pole
    +		result.x = -2 * atan2(q1.x,q1.w);
    +		result.y = -PI/2;
    +	} else {
    +        result.x = atan2(2*q1.y*q1.w-2*q1.x*q1.z , sqx - sqy - sqz + sqw);
    +        result.y = asin(2*test/unit);
    +        result.z = atan2(2*q1.x*q1.w-2*q1.y*q1.z , -sqx + sqy - sqz + sqw);
    +	}
    +	return result;
    +}
    +
    +// Convert float3 rotations in degrees to radians
    +void DegreesToRadianAngles( float3& angles )
    +{
    +    angles.x *= DEGTORAD;
    +    angles.y *= DEGTORAD;
    +    angles.z *= DEGTORAD;
    +}
    +
    +class AssLogStream : public Assimp::LogStream
    +{
    +public:
    +	AssLogStream() {}
    +	~AssLogStream() {}
    +	void write(const char* message)
    +	{
    +		logOutput.Print (LOG_MODEL_DETAIL, "Assimp: %s", message);
    +	}
    +};
    +
    +
    +
    +S3DModel* CAssParser::Load(const std::string& modelFileName)
    +{
    +	logOutput.Print (LOG_MODEL, "Loading model: %s\n", modelFileName.c_str() );
    +	std::string modelPath = modelFileName.substr(0, modelFileName.find_last_of('/'));
    +    std::string modelFileNameNoPath = modelFileName.substr(modelPath.length()+1, modelFileName.length());
    +    std::string modelName = modelFileNameNoPath.substr(0, modelFileNameNoPath.find_last_of('.'));
    +    std::string modelExt = modelFileNameNoPath.substr(modelFileNameNoPath.find_last_of('.'), modelFileName.length());
    +
    +    // LOAD METADATA
    +	// Load the lua metafile. This contains properties unique to Spring models and must return a table
    +	std::string metaFileName = modelFileName + ".lua";
    +	CFileHandler* metaFile = new CFileHandler(metaFileName);
    +	if (!metaFile->FileExists()) {
    +	    // Try again without the model file extension
    +        metaFileName = modelPath + '/' + modelName + ".lua";
    +        metaFile = new CFileHandler(metaFileName);
    +	}
    +	LuaParser metaFileParser(metaFileName, SPRING_VFS_MOD_BASE, SPRING_VFS_ZIP);
    +	if (!metaFileParser.Execute()) {
    +		if (!metaFile->FileExists()) {
    +			logOutput.Print(LOG_MODEL, "No meta-file '%s'. Using defaults.", metaFileName.c_str());
    +		} else {
    +			logOutput.Print(LOG_MODEL, "ERROR in '%s': %s. Using defaults.", metaFileName.c_str(), metaFileParser.GetErrorLog().c_str());
    +		}
    +	}
    +	// Get the (root-level) model table
    +	const LuaTable& metaTable = metaFileParser.GetRoot();
    +    if (metaTable.IsValid()) logOutput.Print(LOG_MODEL, "Found valid model metadata in '%s'", metaFileName.c_str());
    +
    +
    +    // LOAD MODEL DATA
    +	// Create a model importer instance
    + 	Assimp::Importer importer;
    +
    +	// Create a logger for debugging model loading issues
    +	Assimp::DefaultLogger::create("",Assimp::Logger::VERBOSE);
    +	const unsigned int severity = Assimp::Logger::DEBUGGING|Assimp::Logger::INFO|Assimp::Logger::ERR|Assimp::Logger::WARN;
    +	Assimp::DefaultLogger::get()->attachStream( new AssLogStream(), severity );
    +
    +	// Give the importer an IO class that handles Spring's VFS
    +	importer.SetIOHandler( new AssVFSSystem() );
    +
    +    // Speed-up processing by skipping things we don't need
    +    importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, aiComponent_CAMERAS|aiComponent_LIGHTS|aiComponent_TEXTURES|aiComponent_ANIMATIONS);
    +
    +	// Read the model file to build a scene object
    +	logOutput.Print(LOG_MODEL, "Importing model file: %s\n", modelFileName.c_str() );
    +	const aiScene* scene = importer.ReadFile( modelFileName, ASS_POSTPROCESS_OPTIONS );
    +	if (scene != NULL) {
    +		logOutput.Print(LOG_MODEL, "Processing scene for model: %s (%d meshes / %d materials / %d textures)", modelFileName.c_str(), scene->mNumMeshes, scene->mNumMaterials, scene->mNumTextures );
    +    } else {
    +		logOutput.Print (LOG_MODEL, "Model Import Error: %s\n",  importer.GetErrorString());
    +	}
    +
    +    S3DModel* model = new S3DModel;
    +    model->name = modelFileName;
    +	model->type = MODELTYPE_ASS;
    +	model->numPieces = 0;
    +	model->scene = scene;
    +    //model->meta = &metaTable;
    +
    +    // Assign textures
    +    // The S3O texture handler uses two textures.
    +    // The first contains diffuse color (RGB) and teamcolor (A)
    +    // The second contains glow (R), reflectivity (G) and 1-bit Alpha (A).
    +    if (metaTable.KeyExists("tex1")) {
    +        model->tex1 = metaTable.GetString("tex1", "default.png");
    +    } else {
    +        // Search for a texture
    +        std::vector<std::string> files = CFileHandler::FindFiles("unittextures/", modelName + ".*");
    +        for(std::vector<std::string>::iterator fi = files.begin(); fi != files.end(); ++fi) {
    +            std::string texPath = std::string(*fi);
    +            model->tex1 = texPath.substr(texPath.find('/')+1, texPath.length());
    +            break; // there can be only one!
    +        }
    +    }
    +    if (metaTable.KeyExists("tex2")) {
    +        model->tex2 = metaTable.GetString("tex2", "");
    +    } else {
    +        // Search for a texture
    +        std::vector<std::string> files = CFileHandler::FindFiles("unittextures/", modelName + "2.*");
    +        for(std::vector<std::string>::iterator fi = files.begin(); fi != files.end(); ++fi) {
    +            std::string texPath = std::string(*fi);
    +            model->tex2 = texPath.substr(texPath.find('/')+1, texPath.length());
    +            break; // there can be only one!
    +        }
    +    }
    +    model->flipTexY = metaTable.GetBool("fliptextures", true); // Flip texture upside down
    +    model->invertTexAlpha = metaTable.GetBool("invertteamcolor", true); // Reverse teamcolor levels
    +
    +    // Load textures
    +    logOutput.Print(LOG_MODEL, "Loading textures. Tex1: '%s' Tex2: '%s'", model->tex1.c_str(), model->tex2.c_str());
    +    texturehandlerS3O->LoadS3OTexture(model);
    +
    +    // Load all pieces in the model
    +    logOutput.Print(LOG_MODEL, "Loading pieces from root node '%s'", scene->mRootNode->mName.data);
    +    LoadPiece( model, scene->mRootNode, metaTable );
    +
    +    // Update piece hierarchy based on metadata
    +    BuildPieceHierarchy( model );
    +
    +    // Simplified dimensions used for rough calculations
    +    model->radius = metaTable.GetFloat("radius", model->radius);
    +    model->height = metaTable.GetFloat("height", model->height);
    +    model->relMidPos = metaTable.GetFloat3("midpos", model->relMidPos);
    +    model->mins = metaTable.GetFloat3("mins", model->mins);
    +    model->maxs = metaTable.GetFloat3("maxs", model->maxs);
    +
    +    // Calculate model dimensions if not set
    +    if (!metaTable.KeyExists("mins") || !metaTable.KeyExists("maxs")) CalculateMinMax( model->rootPiece );
    +    if (model->radius < 0.0001f) CalculateRadius( model );
    +    if (model->height < 0.0001f) CalculateHeight( model );
    +
    +    // Verbose logging of model properties
    +    logOutput.Print(LOG_MODEL_DETAIL, "model->name: %s", model->name.c_str());
    +    logOutput.Print(LOG_MODEL_DETAIL, "model->numobjects: %d", model->numPieces);
    +    logOutput.Print(LOG_MODEL_DETAIL, "model->radius: %f", model->radius);
    +    logOutput.Print(LOG_MODEL_DETAIL, "model->height: %f", model->height);
    +    logOutput.Print(LOG_MODEL_DETAIL, "model->mins: (%f,%f,%f)", model->mins[0], model->mins[1], model->mins[2]);
    +    logOutput.Print(LOG_MODEL_DETAIL, "model->maxs: (%f,%f,%f)", model->maxs[0], model->maxs[1], model->maxs[2]);
    +
    +    logOutput.Print (LOG_MODEL, "Model %s Imported.", model->name.c_str());
    +    return model;
    +}
    +
    +SAssPiece* CAssParser::LoadPiece(S3DModel* model, aiNode* node, const LuaTable& metaTable)
    +{
    +	// Create new piece
    +	model->numPieces++;
    +	SAssPiece* piece = new SAssPiece;
    +	piece->type = MODELTYPE_OTHER;
    +	piece->node = node;
    +	piece->model = model;
    +	piece->isEmpty = node->mNumMeshes == 0;
    +
    +    if (node->mParent) {
    +        piece->name = std::string(node->mName.data);
    +	} else {
    +        piece->name = "root"; // The real model root
    +	}
    +	logOutput.Print(LOG_PIECE, "Converting node '%s' to piece '%s' (%d meshes).", node->mName.data, piece->name.c_str(), node->mNumMeshes);
    +
    +    // Load additional piece properties from metadata
    +    const LuaTable& pieceTable = metaTable.SubTable("pieces").SubTable(piece->name);
    +    if (pieceTable.IsValid()) logOutput.Print(LOG_PIECE, "Found metadata for piece '%s'", piece->name.c_str());
    +
    +    // Process transforms
    +    float3 rotate, scale, offset;
    +	aiVector3D _scale, _offset;
    + 	aiQuaternion _rotate;
    +	node->mTransformation.Decompose(_scale,_rotate,_offset);
    +
    +	logOutput.Print(LOG_PIECE, "(%d:%s) Assimp offset (%f,%f,%f), rotate (%f,%f,%f), scale (%f,%f,%f)", model->numPieces, piece->name.c_str(),
    +		_offset.x, _offset.y, _offset.z,
    +		_rotate.x, _rotate.y, _rotate.z,
    +		_scale.x, _scale.y, _scale.z
    +	);
    +
    +	offset = pieceTable.GetFloat3("offset", float3(_offset.x, _offset.y, _offset.z));
    +    offset.x = pieceTable.GetFloat("offsetx", offset.x);
    +    offset.y = pieceTable.GetFloat("offsety", offset.y);
    +    offset.z = pieceTable.GetFloat("offsetz", offset.z);
    +
    +    if (pieceTable.KeyExists("rotate")) {
    +        rotate = pieceTable.GetFloat3("rotate", float3(0.0f, 0.0f, 0.0f));
    +        DegreesToRadianAngles(rotate);
    +    } else {
    +        rotate = QuaternionToRadianAngles(_rotate);
    +        rotate = float3(rotate.z, rotate.x, rotate.y);
    +    }
    +    if (pieceTable.KeyExists("rotatex")) rotate.x = pieceTable.GetFloat("rotatex", 0.0f) * DEGTORAD;
    +    if (pieceTable.KeyExists("rotatey")) rotate.y = pieceTable.GetFloat("rotatey", 0.0f) * DEGTORAD;
    +    if (pieceTable.KeyExists("rotatez")) rotate.z = pieceTable.GetFloat("rotatez", 0.0f) * DEGTORAD;
    +
    +	scale = pieceTable.GetFloat3("scale", float3(_scale.x, _scale.z, _scale.y));
    +    scale.x = pieceTable.GetFloat("scalex", scale.x);
    +    scale.y = pieceTable.GetFloat("scaley", scale.y);
    +    scale.z = pieceTable.GetFloat("scalez", scale.z);
    +
    +	logOutput.Print(LOG_PIECE, "(%d:%s) Relative offset (%f,%f,%f), rotate (%f,%f,%f), scale (%f,%f,%f)", model->numPieces, piece->name.c_str(),
    +		offset.x, offset.y, offset.z,
    +		rotate.x, rotate.y, rotate.z,
    +		scale.x, scale.y, scale.z
    +	);
    +	piece->offset = offset;
    +	piece->rot = rotate;
    +	piece->scale = scale;
    +
    +	// Get vertex data from node meshes
    +	for ( unsigned meshListIndex = 0; meshListIndex < node->mNumMeshes; meshListIndex++ ) {
    +		unsigned int meshIndex = node->mMeshes[meshListIndex];
    +		logOutput.Print(LOG_PIECE_DETAIL, "Fetching mesh %d from scene", meshIndex );
    +		aiMesh* mesh = model->scene->mMeshes[meshIndex];
    +		std::vector<unsigned> mesh_vertex_mapping;
    +		// extract vertex data
    +		logOutput.Print(LOG_PIECE_DETAIL, "Processing vertices for mesh %d (%d vertices)", meshIndex, mesh->mNumVertices );
    +		logOutput.Print(LOG_PIECE_DETAIL, "Normals: %s Tangents/Bitangents: %s TexCoords: %s",
    +				(mesh->HasNormals())?"Y":"N",
    +				(mesh->HasTangentsAndBitangents())?"Y":"N",
    +				(mesh->HasTextureCoords(0)?"Y":"N")
    +		);
    +		for ( unsigned vertexIndex= 0; vertexIndex < mesh->mNumVertices; vertexIndex++) {
    +			SAssVertex vertex;
    +
    +			// vertex coordinates
    +			logOutput.Print(LOG_PIECE_DETAIL, "Fetching vertex %d from mesh", vertexIndex );
    +			aiVector3D& aiVertex = mesh->mVertices[vertexIndex];
    +			vertex.pos.x = aiVertex.x;
    +			vertex.pos.y = aiVertex.y;
    +			vertex.pos.z = aiVertex.z;
    +
    +			// update piece min/max extents
    +			piece->mins.x = std::min(piece->mins.x, aiVertex.x);
    +			piece->mins.y = std::min(piece->mins.y, aiVertex.y);
    +			piece->mins.z = std::min(piece->mins.z, aiVertex.z);
    +			piece->maxs.x = std::max(piece->maxs.x, aiVertex.x);
    +			piece->maxs.y = std::max(piece->maxs.y, aiVertex.y);
    +			piece->maxs.z = std::max(piece->maxs.z, aiVertex.z);
    +
    +			logOutput.Print(LOG_PIECE_DETAIL, "vertex position %d: %f %f %f", vertexIndex, vertex.pos.x, vertex.pos.y, vertex.pos.z );
    +
    +			// vertex normal
    +			logOutput.Print(LOG_PIECE_DETAIL, "Fetching normal for vertex %d", vertexIndex );
    +			aiVector3D& aiNormal = mesh->mNormals[vertexIndex];
    +			vertex.hasNormal = !IS_QNAN(aiNormal);
    +			if ( vertex.hasNormal ) {
    +				vertex.normal.x = aiNormal.x;
    +				vertex.normal.y = aiNormal.y;
    +				vertex.normal.z = aiNormal.z;
    +				logOutput.Print(LOG_PIECE_DETAIL, "vertex normal %d: %f %f %f",vertexIndex, vertex.normal.x, vertex.normal.y,vertex.normal.z );
    +			}
    +
    +			// vertex tangent, x is positive in texture axis
    +			if (mesh->HasTangentsAndBitangents()) {
    +				logOutput.Print(LOG_PIECE_DETAIL, "Fetching tangent for vertex %d", vertexIndex );
    +				aiVector3D& aiTangent = mesh->mTangents[vertexIndex];
    +				vertex.hasTangent = !IS_QNAN(aiTangent);
    +				if ( vertex.hasTangent ) {
    +					float3 tangent;
    +					tangent.x = aiTangent.x;
    +					tangent.y = aiTangent.y;
    +					tangent.z = aiTangent.z;
    +					logOutput.Print(LOG_PIECE_DETAIL, "vertex tangent %d: %f %f %f",vertexIndex, tangent.x, tangent.y,tangent.z );
    +					piece->sTangents.push_back(tangent);
    +					// bitangent is cross product of tangent and normal
    +					float3 bitangent;
    +					if ( vertex.hasNormal ) {
    +						bitangent = tangent.cross(vertex.normal);
    +						logOutput.Print(LOG_PIECE_DETAIL, "vertex bitangent %d: %f %f %f",vertexIndex, bitangent.x, bitangent.y,bitangent.z );
    +						piece->tTangents.push_back(bitangent);
    +					}
    +				}
    +			} else {
    +				vertex.hasTangent = false;
    +			}
    +
    +			// vertex texcoords
    +			if (mesh->HasTextureCoords(0)) {
    +				vertex.textureX = mesh->mTextureCoords[0][vertexIndex].x;
    +				vertex.textureY = mesh->mTextureCoords[0][vertexIndex].y;
    +				logOutput.Print(LOG_PIECE_DETAIL, "vertex texcoords %d: %f %f", vertexIndex, vertex.textureX, vertex.textureY );
    +			}
    +
    +			mesh_vertex_mapping.push_back(piece->vertices.size());
    +			piece->vertices.push_back(vertex);
    +		}
    +
    +		// extract face data
    +		if ( mesh->HasFaces() ) {
    +			logOutput.Print(LOG_PIECE_DETAIL, "Processing faces for mesh %d (%d faces)", meshIndex, mesh->mNumFaces);
    +			for ( unsigned faceIndex = 0; faceIndex < mesh->mNumFaces; faceIndex++ ) {
    +                aiFace& face = mesh->mFaces[faceIndex];
    +				// get the vertex belonging to the mesh
    +				for ( unsigned vertexListID = 0; vertexListID < face.mNumIndices; vertexListID++ ) {
    +					unsigned int vertexID = mesh_vertex_mapping[face.mIndices[vertexListID]];
    +					logOutput.Print(LOG_PIECE_DETAIL, "face %d vertex %d", faceIndex, vertexID );
    +					piece->vertexDrawOrder.push_back(vertexID);
    +				}
    +			}
    +		}
    +	}
    +
    +    // Check if piece is special (ie, used to set Spring model properties)
    +    if (strcmp(node->mName.data, "SpringHeight") == 0) {
    +        // Set the model height to this nodes Z value
    +        if (!metaTable.KeyExists("height")) {
    +            model->height = piece->offset.z;
    +            logOutput.Print (LOG_MODEL, "Model height of %f set by special node 'SpringHeight'", model->height);
    +        }
    +        return NULL;
    +    }
    +    if (strcmp(node->mName.data, "SpringRadius") == 0) {
    +        if (!metaTable.KeyExists("midpos")) {
    +            model->relMidPos = float3(piece->offset.x, piece->offset.z, piece->offset.y); // Y and Z are swapped because this piece isn't rotated
    +            logOutput.Print (LOG_MODEL, "Model midpos of (%f,%f,%f) set by special node 'SpringRadius'", model->relMidPos.x, model->relMidPos.y, model->relMidPos.z);
    +        }
    +        if (!metaTable.KeyExists("radius")) {
    +            if (piece->maxs.x <= 0.00001f) {
    +                model->radius = piece->scale.x; // the blender import script only sets the scale property
    +            } else {
    +                model->radius = piece->maxs.x; // use the transformed mesh extents
    +            }
    +            logOutput.Print (LOG_MODEL, "Model radius of %f set by special node 'SpringRadius'", model->radius);
    +        }
    +        return NULL;
    +    }
    +
    +	// collision volume for piece (not sure about these coords)
    +	const float3 cvScales = (piece->maxs) - (piece->mins);
    +	const float3 cvOffset = (piece->maxs - piece->offset) + (piece->mins - piece->offset);
    +	//const float3 cvOffset(piece->offset.x, piece->offset.y, piece->offset.z);
    +	piece->colvol = new CollisionVolume("box", cvScales, cvOffset, CollisionVolume::COLVOL_HITTEST_CONT);
    +
    +    // Get parent name from metadata or model
    +    if (pieceTable.KeyExists("parent")) {
    +        piece->parentName = pieceTable.GetString("parent", "");
    +    } else if (node->mParent) {
    +        if (node->mParent->mParent) {
    +            piece->parentName = std::string(node->mParent->mName.data);
    +        } else { // my parent is the root, which gets renamed
    +            piece->parentName = "root";
    +        }
    +    } else {
    +        piece->parentName = "";
    +    }
    +
    +    logOutput.Print(LOG_PIECE, "Loaded model piece: %s with %d meshes\n", piece->name.c_str(), node->mNumMeshes );
    +
    +    // Verbose logging of piece properties
    +    logOutput.Print(LOG_PIECE, "piece->name: %s", piece->name.c_str());
    +    logOutput.Print(LOG_PIECE, "piece->parent: %s", piece->parentName.c_str());
    +
    +	// Recursively process all child pieces
    +	for (unsigned int i = 0; i < node->mNumChildren; ++i) {
    +		LoadPiece(model, node->mChildren[i], metaTable);
    +	}
    +
    +	model->pieces[piece->name] = piece;
    +	return piece;
    +}
    +
    +// Because of metadata overrides we don't know the true hierarchy until all pieces have been loaded
    +void CAssParser::BuildPieceHierarchy( S3DModel* model )
    +{
    +    // Loop through all pieces and create missing hierarchy info
    +    ModelPieceMap::const_iterator end = model->pieces.end();
    +    for (ModelPieceMap::const_iterator it = model->pieces.begin(); it != end; ++it)
    +    {
    +        S3DModelPiece* piece = it->second;
    +        if (piece->name == "root") {
    +            piece->parent = NULL;
    +            model->SetRootPiece(piece);
    +        } else if (piece->parentName != "") {
    +            piece->parent = model->FindPiece(piece->parentName);
    +            if (piece->parent == NULL) {
    +                logOutput.Print( LOG_PIECE, "Error: Missing piece '%s' declared as parent of '%s'.\n", piece->parentName.c_str(), piece->name.c_str() );
    +            } else {
    +                piece->parent->childs.push_back(piece);
    +            }
    +        } else {
    +            // A piece with no parent that isn't the root (orphan)
    +            piece->parent = model->FindPiece("root");
    +            if (piece->parent == NULL) {
    +                logOutput.Print( LOG_PIECE, "Error: Missing root piece.\n" );
    +            } else {
    +                piece->parent->childs.push_back(piece);
    +            }
    +        }
    +    }
    +}
    +
    +// Iterate over the model and calculate its overall dimensions
    +void CAssParser::CalculateMinMax( S3DModelPiece* piece )
    +{
    +    piece->goffset = piece->parent ? piece->parent->goffset + piece->offset : piece->offset;
    +
    +	// update model min/max extents
    +	piece->model->mins.x = std::min(piece->goffset.x + piece->mins.x, piece->model->mins.x);
    +	piece->model->mins.y = std::min(piece->goffset.y + piece->mins.y, piece->model->mins.y);
    +	piece->model->mins.z = std::min(piece->goffset.z + piece->mins.z, piece->model->mins.z);
    +	piece->model->maxs.x = std::max(piece->goffset.x + piece->maxs.x, piece->model->maxs.x);
    +	piece->model->maxs.y = std::max(piece->goffset.y + piece->maxs.y, piece->model->maxs.y);
    +	piece->model->maxs.z = std::max(piece->goffset.z + piece->maxs.z, piece->model->maxs.z);
    +
    +	// Repeat with childs
    +	for (unsigned int i = 0; i < piece->childs.size(); i++) {
    +		CalculateMinMax(piece->childs[i]);
    +	}
    +}
    +
    +// Calculate model radius from the min/max extents
    +void CAssParser::CalculateRadius( S3DModel* model )
    +{
    +    model->radius = std::max(model->radius, model->maxs.x);
    +    model->radius = std::max(model->radius, model->maxs.y);
    +    model->radius = std::max(model->radius, model->maxs.z);
    +}
    +
    +// Calculate model height from the min/max extents
    +void CAssParser::CalculateHeight( S3DModel* model )
    +{
    +    model->height = model->maxs.z;
    +}
    +
    +void DrawPiecePrimitive( const S3DModelPiece* o)
    +{
    +	if (o->isEmpty) {
    +		return;
    +	}
    +	logOutput.Print(LOG_PIECE_DETAIL, "Compiling piece %s", o->name.c_str());
    +	// Add GL commands to the pieces displaylist
    +
    +	const SAssPiece* so = static_cast<const SAssPiece*>(o);
    +	const SAssVertex* sAssV = static_cast<const SAssVertex*>(&so->vertices[0]);
    +
    +
    +	// pass the tangents as 3D texture coordinates
    +	// (array elements are float3's, which are 12
    +	// bytes in size and each represent a single
    +	// xyz triple)
    +	// TODO: test if we have this many texunits
    +	// (if not, could only send the s-tangents)?
    +
    +	if (!so->sTangents.empty()) {
    +		glClientActiveTexture(GL_TEXTURE5);
    +		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    +		glTexCoordPointer(3, GL_FLOAT, sizeof(float3), &so->sTangents[0].x);
    +	}
    +	if (!so->tTangents.empty()) {
    +		glClientActiveTexture(GL_TEXTURE6);
    +		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    +		glTexCoordPointer(3, GL_FLOAT, sizeof(float3), &so->tTangents[0].x);
    +	}
    +
    +	glClientActiveTexture(GL_TEXTURE0);
    +	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    +	glTexCoordPointer(2, GL_FLOAT, sizeof(SAssVertex), &sAssV->textureX);
    +
    +	glEnableClientState(GL_VERTEX_ARRAY);
    +	glVertexPointer(3, GL_FLOAT, sizeof(SAssVertex), &sAssV->pos.x);
    +
    +	glEnableClientState(GL_NORMAL_ARRAY);
    +	glNormalPointer(GL_FLOAT, sizeof(SAssVertex), &sAssV->normal.x);
    +
    +	// since aiProcess_SortByPType is being used, we're sure we'll get only 1 type here, so combination check isn't needed, also anything more complex than triangles is being split thanks to aiProcess_Triangulate
    +	glDrawElements(GL_TRIANGLES, so->vertexDrawOrder.size(), GL_UNSIGNED_INT, &so->vertexDrawOrder[0]);
    +
    +	if (!so->sTangents.empty()) {
    +		glClientActiveTexture(GL_TEXTURE6);
    +		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    +	}
    +	if (!so->tTangents.empty()) {
    +		glClientActiveTexture(GL_TEXTURE5);
    +		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    +	}
    +
    +	glClientActiveTexture(GL_TEXTURE0);
    +	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    +
    +	glDisableClientState(GL_VERTEX_ARRAY);
    +	glDisableClientState(GL_NORMAL_ARRAY);
    +
    +	logOutput.Print(LOG_PIECE_DETAIL, "Completed compiling piece %s", o->name.c_str());
    +}
    +
    +void CAssParser::Draw( const S3DModelPiece* o) const
    +{
    +	DrawPiecePrimitive( o );
    +}
    +
    +void SAssPiece::DrawList() const
    +{
    +	DrawPiecePrimitive(this);
    +}
    diff --git a/rts/Rendering/Models/AssParser.h b/rts/Rendering/Models/AssParser.h
    new file mode 100755
    index 0000000..e7a5b70
    --- /dev/null
    +++ b/rts/Rendering/Models/AssParser.h
    @@ -0,0 +1,51 @@
    +#ifndef ASSPARSER_H
    +#define ASSPARSER_H
    +
    +#include <map>
    +#include "IModelParser.h"
    +#include "float3.h"
    +
    +struct aiNode;
    +struct aiScene;
    +class LuaTable;
    +
    +struct SAssVertex {
    +	float3 pos;
    +	float3 normal;
    +	float textureX;
    +	float textureY;
    +	bool hasNormal;
    +	bool hasTangent;
    +};
    +
    +struct SAssPiece: public S3DModelPiece
    +{
    +public:
    +	aiNode* node;
    +
    +	const float3& GetVertexPos(int idx) const { return vertices[idx].pos; }
    +	void DrawList() const;
    +	std::vector<SAssVertex> vertices;
    +
    +	std::vector<unsigned int> vertexDrawOrder;
    +	// cannot store these in SAssVertex
    +	std::vector<float3> sTangents; // == T(angent) dirs
    +	std::vector<float3> tTangents; // == B(itangent) dirs
    +};
    +
    +
    +class CAssParser: public IModelParser
    +{
    +public:
    +	S3DModel* Load(const std::string& modelFileName);
    +	void Draw(const S3DModelPiece* o) const;
    +
    +private:
    +	SAssPiece* LoadPiece(S3DModel* model, aiNode* node, const LuaTable& metaTable);
    +	void BuildPieceHierarchy(S3DModel* model);
    +    void CalculateRadius( S3DModel* model );
    +    void CalculateHeight( S3DModel* model );
    +    void CalculateMinMax( S3DModelPiece* piece );
    +};
    +
    +#endif /* ASSPARSER_H */
    diff --git a/rts/Rendering/Models/IModelParser.cpp b/rts/Rendering/Models/IModelParser.cpp
    old mode 100644
    new mode 100755
    index aff59ad..e9513ee
    --- a/rts/Rendering/Models/IModelParser.cpp
    +++ b/rts/Rendering/Models/IModelParser.cpp
    @@ -8,9 +8,12 @@
     
     #include "IModelParser.h"
     #include "3DModel.h"
    +#include "3DModelLog.h"
     #include "3DOParser.h"
     #include "S3OParser.h"
     #include "OBJParser.h"
    +#include "AssParser.h"
    +#include "assimp.hpp"
     #include "Sim/Misc/CollisionVolume.h"
     #include "Sim/Units/Unit.h"
     #include "System/FileSystem/FileSystem.h"
    @@ -18,10 +21,8 @@
     #include "System/LogOutput.h"
     #include "System/Exceptions.h"
     
    -
     C3DModelLoader* modelParser = NULL;
     
    -
     //////////////////////////////////////////////////////////////////////
     // C3DModelLoader
     //
    @@ -31,7 +32,26 @@ C3DModelLoader::C3DModelLoader()
     	// file-extension should be lowercase
     	parsers["3do"] = new C3DOParser();
     	parsers["s3o"] = new CS3OParser();
    -	parsers["obj"] = new COBJParser();
    +	//parsers["obj"] = new COBJParser(); // replaced by Assimp
    +
    +	// assimp library
    +	CAssParser* unitassparser = new CAssParser();
    +	std::string extensionlist;
    +
    +	Assimp::Importer importer;
    +	importer.GetExtensionList(extensionlist); // get a ";" separated list of wildcards
    +	char* charextensionlist = new char[extensionlist.size() +1];
    +	strcpy (charextensionlist, extensionlist.c_str());
    +	logOutput.Print("Assimp: Supported model formats: %s", extensionlist.c_str());
    +	char* extensionchar = strtok( charextensionlist, ";" );
    +	while( extensionchar )
    +	{
    +		std::string extension = extensionchar;
    +		extension = extension.substr( 2 ); // strip wildcard and dot
    +		parsers[extension] = unitassparser; // register extension
    +		extensionchar = strtok( NULL, ";" );
    +	}
    +	delete charextensionlist;
     }
     
     
    @@ -48,11 +68,15 @@ C3DModelLoader::~C3DModelLoader()
     	cache.clear();
     
     	// delete parsers
    +	std::set<IModelParser*> dedupe_parsers; // this is to avoid deleting the same parser twice, if it's assigned to multiple model formats
     	std::map<std::string, IModelParser*>::iterator pi;
     	for (pi = parsers.begin(); pi != parsers.end(); ++pi) {
    +		if ( dedupe_parsers.count( pi->second ) != 0 ) continue;
    +		dedupe_parsers.insert( pi->second );
     		delete pi->second;
     	}
     	parsers.clear();
    +	dedupe_parsers.clear();
     
     #if defined(USE_GML) && GML_ENABLE_SIM
     	createLists.clear();
    @@ -67,7 +91,7 @@ inline int ModelExtToModelType(const std::string& ext) {
     	if (ext == "3do") { return MODELTYPE_3DO; }
     	if (ext == "s3o") { return MODELTYPE_S3O; }
     	if (ext == "obj") { return MODELTYPE_OBJ; }
    -	return -1;
    +	return MODELTYPE_ASS; // FIXME: Return -1 if Assimp cant handle extension
     }
     inline S3DModelPiece* ModelTypeToModelPiece(int type) {
     	if (type == MODELTYPE_3DO) { return (new S3DOPiece()); }
    diff --git a/rts/Rendering/Models/WorldObjectModelRenderer.cpp b/rts/Rendering/Models/WorldObjectModelRenderer.cpp
    old mode 100644
    new mode 100755
    index 778f11b..bd8237b
    --- a/rts/Rendering/Models/WorldObjectModelRenderer.cpp
    +++ b/rts/Rendering/Models/WorldObjectModelRenderer.cpp
    @@ -14,6 +14,7 @@ IWorldObjectModelRenderer* IWorldObjectModelRenderer::GetInstance(int modelType)
     		case MODELTYPE_3DO: { return (new WorldObjectModelRenderer3DO()); } break;
     		case MODELTYPE_S3O: { return (new WorldObjectModelRendererS3O()); } break;
     		case MODELTYPE_OBJ: { return (new WorldObjectModelRendererOBJ()); } break;
    +		case MODELTYPE_ASS: { return (new WorldObjectModelRendererASS()); } break;
     		default: { return (new IWorldObjectModelRenderer(MODELTYPE_OTHER)); } break;
     	}
     }
    @@ -226,7 +227,18 @@ void WorldObjectModelRendererOBJ::PopRenderState()
     	// WRITEME
     }
     
    -
    +void WorldObjectModelRendererASS::PushRenderState()
    +{
    +	#if (WORLDOBJECT_MODEL_RENDERER_DEBUG == 1)
    +	#endif
    +	// WRITEME
    +}
    +void WorldObjectModelRendererASS::PopRenderState()
    +{
    +	#if (WORLDOBJECT_MODEL_RENDERER_DEBUG == 1)
    +	#endif
    +	// WRITEME
    +}
     
     void WorldObjectModelRendererS3O::DrawModel(const CUnit* u)
     {
    diff --git a/rts/Rendering/Models/WorldObjectModelRenderer.h b/rts/Rendering/Models/WorldObjectModelRenderer.h
    old mode 100644
    new mode 100755
    index 386da99..11a7644
    --- a/rts/Rendering/Models/WorldObjectModelRenderer.h
    +++ b/rts/Rendering/Models/WorldObjectModelRenderer.h
    @@ -117,4 +117,11 @@ public:
     	void PopRenderState();
     };
     
    +class WorldObjectModelRendererASS: public IWorldObjectModelRenderer {
    +public:
    +	WorldObjectModelRendererASS(): IWorldObjectModelRenderer(MODELTYPE_ASS) {}
    +	void PushRenderState();
    +	void PopRenderState();
    +};
    +
     #endif
    diff --git a/rts/Rendering/ProjectileDrawer.cpp b/rts/Rendering/ProjectileDrawer.cpp
    old mode 100644
    new mode 100755
    diff --git a/rts/Rendering/Textures/Bitmap.cpp b/rts/Rendering/Textures/Bitmap.cpp
    old mode 100644
    new mode 100755
    index 2445c31..0e31ad6
    --- a/rts/Rendering/Textures/Bitmap.cpp
    +++ b/rts/Rendering/Textures/Bitmap.cpp
    @@ -282,9 +282,9 @@ bool CBitmap::LoadGrayscale(const std::string& filename)
     	delete[] mem;
     	mem = new unsigned char[xsize * ysize];
     	memcpy(mem, ilGetData(), xsize * ysize);
    -	
    +
     	ilDeleteImages(1, &ImageName);
    -	
    +
     	return true;
     }
     
    @@ -738,6 +738,18 @@ void CBitmap::InvertColors()
     }
     
     
    +void CBitmap::InvertAlpha()
    +{
    +	if (type != BitmapTypeStandardRGBA) return; // Don't try to invert DDS
    +	for (int y = 0; y < ysize; ++y) {
    +		for (int x = 0; x < xsize; ++x) {
    +			const int base = ((y * xsize) + x) * 4;
    +			mem[base + 3] = 0xFF - mem[base + 3];
    +		}
    +	}
    +}
    +
    +
     void CBitmap::GrayScale()
     {
     	if (type != BitmapTypeStandardRGBA) {
    @@ -786,6 +798,7 @@ void CBitmap::Tint(const float tint[3])
     
     void CBitmap::ReverseYAxis()
     {
    +    if (type != BitmapTypeStandardRGBA) return; // don't try to flip DDS
     	unsigned char* tmpLine = new unsigned char[channels * xsize];
     	for (int y=0; y < (ysize / 2); ++y) {
     		const int pixelLow  = (((y            ) * xsize) + 0) * channels;
    diff --git a/rts/Rendering/Textures/Bitmap.h b/rts/Rendering/Textures/Bitmap.h
    old mode 100644
    new mode 100755
    index 9b1577b..9d984f9
    --- a/rts/Rendering/Textures/Bitmap.h
    +++ b/rts/Rendering/Textures/Bitmap.h
    @@ -83,6 +83,7 @@ public:
     	CBitmap CreateRescaled(int newx, int newy) const;
     	void ReverseYAxis();
     	void InvertColors();
    +	void InvertAlpha();
     	void GrayScale();
     	void Tint(const float tint[3]);
     };
    diff --git a/rts/Rendering/Textures/S3OTextureHandler.cpp b/rts/Rendering/Textures/S3OTextureHandler.cpp
    old mode 100644
    new mode 100755
    index 857406b..abb6a02
    --- a/rts/Rendering/Textures/S3OTextureHandler.cpp
    +++ b/rts/Rendering/Textures/S3OTextureHandler.cpp
    @@ -20,6 +20,15 @@
     #include "System/Exceptions.h"
     #include "System/LogOutput.h"
     
    +static CLogSubsystem LOG_TEXTURE("Texture");
    +
    +// The S3O texture handler uses two textures.
    +// The first contains diffuse color (RGB) and teamcolor (A)
    +// The second contains glow (R), reflectivity (G) and 1-bit Alpha (A).
    +
    +//////////////////////////////////////////////////////////////////////
    +// Construction/Destruction
    +//////////////////////////////////////////////////////////////////////
     
     CS3OTextureHandler* texturehandlerS3O = NULL;
     
    @@ -52,6 +61,10 @@ void CS3OTextureHandler::Update() {
     int CS3OTextureHandler::LoadS3OTextureNow(const S3DModel* model)
     {
     	GML_STDMUTEX_LOCK(model); // LoadS3OTextureNow
    +	logOutput.Print(LOG_TEXTURE, "Load S3O texture now (Flip Y Axis: %s, Invert Team Alpha: %s)", 
    +		model->flipTexY ? "yes" : "no",
    +		model->invertTexAlpha ? "yes" : "no"
    +	);
     
     	const string totalName = model->tex1 + model->tex2;
     
    @@ -74,6 +87,8 @@ int CS3OTextureHandler::LoadS3OTextureNow(const S3DModel* model)
     		tex1bm.mem[2] =   0;
     		tex1bm.mem[3] = 255;
     	}
    +	if (model->flipTexY) tex1bm.ReverseYAxis();
    +	if (model->invertTexAlpha) tex1bm.InvertAlpha();
     
     	tex.num       = s3oTextures.size();
     	tex.tex1      = tex1bm.CreateTexture(true);
    @@ -92,6 +107,7 @@ int CS3OTextureHandler::LoadS3OTextureNow(const S3DModel* model)
     		tex2bm.mem[2] =   0; // unused
     		tex2bm.mem[3] = 255; // team-color
     	}
    +	if (model->flipTexY) tex2bm.ReverseYAxis();
     
     	tex.tex2      = tex2bm.CreateTexture(true);
     	tex.tex2SizeX = tex2bm.xsize;
    diff --git a/rts/Rendering/Textures/S3OTextureHandler.h b/rts/Rendering/Textures/S3OTextureHandler.h
    old mode 100644
    new mode 100755
    diff --git a/rts/Rendering/UnitDrawer.cpp b/rts/Rendering/UnitDrawer.cpp
    old mode 100644
    new mode 100755
    index 9dc973b..f6ca177
    --- a/rts/Rendering/UnitDrawer.cpp
    +++ b/rts/Rendering/UnitDrawer.cpp
    @@ -519,7 +519,7 @@ void CUnitDrawer::DrawOpaqueUnits(int modelType, const CUnit* excludeUnit, bool
     	UnitSet::const_iterator unitSetIt;
     
     	for (unitBinIt = unitBin.begin(); unitBinIt != unitBin.end(); ++unitBinIt) {
    -		if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ) {
    +		if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ || modelType == MODELTYPE_ASS) {
     			texturehandlerS3O->SetS3oTexture(unitBinIt->first);
     		}
     
    @@ -779,13 +779,13 @@ inline void CUnitDrawer::DrawOpaqueUnitShadow(CUnit* unit) {
     		#define S3O_TEX(model) \
     			texturehandlerS3O->GetS3oTex(model->textureType)
     		#define PUSH_SHADOW_TEXTURE_STATE(model)                                  \
    -			if (model->type == MODELTYPE_S3O || model->type == MODELTYPE_OBJ) {   \
    +			if (model->type == MODELTYPE_S3O || model->type == MODELTYPE_OBJ || model->type == MODELTYPE_ASS) {   \
     				glActiveTexture(GL_TEXTURE0);                                     \
     				glEnable(GL_TEXTURE_2D);                                          \
     				glBindTexture(GL_TEXTURE_2D, S3O_TEX(model)->tex2);               \
     			}
     		#define POP_SHADOW_TEXTURE_STATE(model)                                   \
    -			if (model->type == MODELTYPE_S3O || model->type == MODELTYPE_OBJ) {   \
    +			if (model->type == MODELTYPE_S3O || model->type == MODELTYPE_OBJ || model->type == MODELTYPE_ASS) {   \
     				glBindTexture(GL_TEXTURE_2D, 0);                                  \
     				glDisable(GL_TEXTURE_2D);                                         \
     				glActiveTexture(GL_TEXTURE0);                                     \
    @@ -1042,7 +1042,7 @@ void CUnitDrawer::DrawCloakedUnitsHelper(int modelType)
     
     		// cloaked units
     		for (UnitRenderBinIt it = unitBin.begin(); it != unitBin.end(); ++it) {
    -			if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ) {
    +			if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ || modelType == MODELTYPE_ASS) {
     				texturehandlerS3O->SetS3oTexture(it->first);
     			}
     
    @@ -1099,7 +1099,7 @@ inline void CUnitDrawer::DrawCloakedUnit(CUnit* unit, int modelType, bool drawGh
     		glTranslatef3(unit->pos);
     		glRotatef(unit->buildFacing * 90.0f, 0, 1, 0);
     
    -		if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ) {
    +		if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ || modelType == MODELTYPE_ASS) {
     			// the units in liveGhostedBuildings[modelType] are not
     			// sorted by textureType, but we cannot merge them with
     			// cloakedModelRenderers[modelType] since they are not
    @@ -1183,7 +1183,7 @@ void CUnitDrawer::DrawGhostedBuildings(int modelType)
     				glTranslatef3((*it)->pos);
     				glRotatef((*it)->facing * 90.0f, 0, 1, 0);
     
    -				if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ)
    +				if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ || modelType == MODELTYPE_ASS)
     					texturehandlerS3O->SetS3oTexture((*it)->model->textureType);
     
     				SetTeamColour((*it)->team, cloakAlpha1);
    @@ -1559,7 +1559,7 @@ void CUnitDrawer::DrawIndividual(CUnit* unit)
     		SetupForUnitDrawing();
     		opaqueModelRenderers[MDL_TYPE(unit)]->PushRenderState();
     
    -		if (MDL_TYPE(unit) == MODELTYPE_S3O || MDL_TYPE(unit) == MODELTYPE_OBJ) {
    +		if (MDL_TYPE(unit) == MODELTYPE_S3O || MDL_TYPE(unit) == MODELTYPE_OBJ || MDL_TYPE(unit) == MODELTYPE_ASS) {
     			texturehandlerS3O->SetS3oTexture(TEX_TYPE(unit));
     		}
     
    @@ -1599,7 +1599,8 @@ void CUnitDrawer::DrawBuildingSample(const UnitDef* unitdef, int side, float3 po
     			texturehandler3DO->Set3doAtlases();
     		} break;
     		case MODELTYPE_S3O:
    -		case MODELTYPE_OBJ: {
    +		case MODELTYPE_OBJ:
    +		case MODELTYPE_ASS: {
     			texturehandlerS3O->SetS3oTexture(model->textureType);
     		} break;
     		default: {
    @@ -1987,7 +1988,7 @@ inline void CUnitDrawer::UpdateUnitIconState(CUnit* unit) {
     #ifdef USE_GML
     		if (showHealthBars && !unit->noDraw &&
     			(unit->health < unit->maxHealth || unit->paralyzeDamage > 0.0f || unit->limExperience > 0.0f ||
    -			unit->beingBuilt || unit->stockpileWeapon || unit->group) && 
    +			unit->beingBuilt || unit->stockpileWeapon || unit->group) &&
     			((unit->pos - camera->pos).SqLength() < (unitDrawDistSqr * 500.0f)))
     			drawStat.insert(unit);
     #endif
    diff --git a/rts/Sim/Misc/GlobalConstants.h b/rts/Sim/Misc/GlobalConstants.h
    index 78c5305..6006a4e 100644
    --- a/rts/Sim/Misc/GlobalConstants.h
    +++ b/rts/Sim/Misc/GlobalConstants.h
    @@ -39,6 +39,13 @@ const int UNIT_SLOWUPDATE_RATE = 16;
     const int TEAM_SLOWUPDATE_RATE = 32;
     
     /**
    + * @brief max view range
    + *
    + * Defines the maximum view range as 8000
    + */
    +const int MAX_VIEW_RANGE = 8000;
    +
    +/**
      * @brief max teams
      *
      * Defines the maximum number of teams
    @@ -56,10 +63,9 @@ const int MAX_PLAYERS = 251;
     /**
      * @brief max units
      *
    - * Defines the absolute global maximum number of units allowed to exist in a game.
    - * MUST be <= SHRT_MAX ((1 << ((sizeof(short) * 8) - 1)) - 1) because current net
    - * code transmits unit ID's as signed shorts. The effective global unit limit is
    - * stored in UnitHandler::maxUnits and is always clamped to this value.
    + * Defines the maximum number of untis that may be set as maximum for a game.
    + * The real maximum of the game is stored in uh->maxUnits,
    + * and may not be higher then this value.
      */
     const int MAX_UNITS = 32000;
     
    @@ -69,6 +75,13 @@ const int MAX_UNITS = 32000;
     const int MAX_WEAPONS_PER_UNIT = 32;
     
     /**
    + * @brief near plane
    + *
    + * Defines the near plane as 2.8f
    + */
    +const float NEAR_PLANE = 2.8f;
    +
    +/**
      * @brief randint max
      *
      * Defines the maximum random integer as 0x7fff
    diff --git a/rts/Sim/Misc/Team.cpp b/rts/Sim/Misc/Team.cpp
    index 407c17f..3b84cc6 100644
    --- a/rts/Sim/Misc/Team.cpp
    +++ b/rts/Sim/Misc/Team.cpp
    @@ -27,52 +27,56 @@
     
     CR_BIND(CTeam,);
     CR_REG_METADATA(CTeam, (
    -	// from CTeamBase
    -	CR_MEMBER(leader),
    -	CR_MEMBER(color),
    -	CR_MEMBER(incomeMultiplier),
    -	CR_MEMBER(side),
    -	CR_MEMBER(startPos),
    -	CR_MEMBER(teamStartNum),
    -	CR_MEMBER(teamAllyteam),
    -	// CR_MEMBER(customValues),
    -	// from CTeam
    -	CR_MEMBER(teamNum),
    -	CR_MEMBER(isDead),
    -	CR_MEMBER(gaia),
    -	CR_MEMBER(origColor),
    -	CR_MEMBER(units),
    -	CR_MEMBER(metal),
    -	CR_MEMBER(energy),
    -	CR_MEMBER(metalPull),
    -	CR_MEMBER(prevMetalPull),
    -	CR_MEMBER(metalIncome),
    -	CR_MEMBER(prevMetalIncome),
    -	CR_MEMBER(metalExpense),
    -	CR_MEMBER(prevMetalExpense),
    -	CR_MEMBER(energyPull),
    -	CR_MEMBER(prevEnergyPull),
    -	CR_MEMBER(energyIncome),
    -	CR_MEMBER(prevEnergyIncome),
    -	CR_MEMBER(energyExpense),
    -	CR_MEMBER(prevEnergyExpense),
    -	CR_MEMBER(metalStorage),
    -	CR_MEMBER(energyStorage),
    -	CR_MEMBER(metalShare),
    -	CR_MEMBER(energyShare),
    -	CR_MEMBER(delayedMetalShare),
    -	CR_MEMBER(delayedEnergyShare),
    -	CR_MEMBER(metalSent),
    -	CR_MEMBER(metalReceived),
    -	CR_MEMBER(energySent),
    -	CR_MEMBER(energyReceived),
    -	//CR_MEMBER(currentStats),
    -	CR_MEMBER(nextHistoryEntry),
    -	//CR_MEMBER(statHistory),
    -	CR_MEMBER(modParams),
    -	CR_MEMBER(modParamsMap),
    -	CR_RESERVED(64)
    -));
    +// from CTeamBase
    +				CR_MEMBER(leader),
    +				CR_MEMBER(color),
    +				CR_MEMBER(incomeMultiplier),
    +				CR_MEMBER(side),
    +				CR_MEMBER(startPos),
    +				CR_MEMBER(teamStartNum),
    +				CR_MEMBER(teamAllyteam),
    +//				CR_MEMBER(customValues),
    +// from CTeam
    +				CR_MEMBER(teamNum),
    +				CR_MEMBER(isDead),
    +				CR_MEMBER(gaia),
    +				CR_MEMBER(origColor),
    +				CR_MEMBER(units),
    +				CR_MEMBER(metal),
    +				CR_MEMBER(energy),
    +				CR_MEMBER(metalPull),
    +				CR_MEMBER(prevMetalPull),
    +				CR_MEMBER(metalIncome),
    +				CR_MEMBER(prevMetalIncome),
    +				CR_MEMBER(metalExpense),
    +				CR_MEMBER(prevMetalExpense),
    +				CR_MEMBER(metalUpkeep),
    +				CR_MEMBER(prevMetalUpkeep),
    +				CR_MEMBER(energyPull),
    +				CR_MEMBER(prevEnergyPull),
    +				CR_MEMBER(energyIncome),
    +				CR_MEMBER(prevEnergyIncome),
    +				CR_MEMBER(energyExpense),
    +				CR_MEMBER(prevEnergyExpense),
    +				CR_MEMBER(energyUpkeep),
    +				CR_MEMBER(prevEnergyUpkeep),
    +				CR_MEMBER(metalStorage),
    +				CR_MEMBER(energyStorage),
    +				CR_MEMBER(metalShare),
    +				CR_MEMBER(energyShare),
    +				CR_MEMBER(delayedMetalShare),
    +				CR_MEMBER(delayedEnergyShare),
    +				CR_MEMBER(metalSent),
    +				CR_MEMBER(metalReceived),
    +				CR_MEMBER(energySent),
    +				CR_MEMBER(energyReceived),
    +				//CR_MEMBER(currentStats),
    +				CR_MEMBER(nextHistoryEntry),
    +				//CR_MEMBER(statHistory),
    +				CR_MEMBER(modParams),
    +				CR_MEMBER(modParamsMap),
    +				CR_RESERVED(64)
    +				));
     
     
     //////////////////////////////////////////////////////////////////////
    @@ -88,9 +92,11 @@ CTeam::CTeam() :
     	metalPull(0.0f),     prevMetalPull(0.0f),
     	metalIncome(0.0f),   prevMetalIncome(0.0f),
     	metalExpense(0.0f),  prevMetalExpense(0.0f),
    +	metalUpkeep(0.0f),   prevMetalUpkeep(0.0f),
     	energyPull(0.0f),    prevEnergyPull(0.0f),
     	energyIncome(0.0f),  prevEnergyIncome(0.0f),
     	energyExpense(0.0f), prevEnergyExpense(0.0f),
    +	energyUpkeep(0.0f),  prevEnergyUpkeep(0.0f),
     	metalStorage(1000000),
     	energyStorage(1000000),
     	metalShare(0.99f),
    @@ -114,10 +120,14 @@ CTeam::CTeam() :
     }
     
     
    +CTeam::~CTeam()
    +{
    +}
    +
     
     bool CTeam::UseMetal(float amount)
     {
    -	if (metal >= amount) {
    +	if ((metal - (prevMetalUpkeep * 10)) >= amount) {
     		metal -= amount;
     		metalExpense += amount;
     		return true;
    @@ -125,9 +135,10 @@ bool CTeam::UseMetal(float amount)
     	return false;
     }
     
    +
     bool CTeam::UseEnergy(float amount)
     {
    -	if (energy >= amount) {
    +	if ((energy - (prevEnergyUpkeep * 10)) >= amount) {
     		energy -= amount;
     		energyExpense += amount;
     		return true;
    @@ -136,6 +147,29 @@ bool CTeam::UseEnergy(float amount)
     }
     
     
    +bool CTeam::UseMetalUpkeep(float amount)
    +{
    +	if (metal >= amount) {
    +		metal -= amount;
    +		metalExpense += amount;
    +		metalUpkeep += amount;
    +		return true;
    +	}
    +	return false;
    +}
    +
    +
    +bool CTeam::UseEnergyUpkeep(float amount)
    +{
    +	if (energy >= amount) {
    +		energy -= amount;
    +		energyExpense += amount;
    +		energyUpkeep += amount;
    +		return true;
    +	}
    +	return false;
    +}
    +
     
     void CTeam::AddMetal(float amount, bool useIncomeMultiplier)
     {
    @@ -143,24 +177,23 @@ void CTeam::AddMetal(float amount, bool useIncomeMultiplier)
     	metal += amount;
     	metalIncome += amount;
     	if (metal > metalStorage) {
    -		delayedMetalShare += (metal - metalStorage);
    +		delayedMetalShare += metal - metalStorage;
     		metal = metalStorage;
     	}
     }
     
    +
     void CTeam::AddEnergy(float amount, bool useIncomeMultiplier)
     {
     	if (useIncomeMultiplier) { amount *= GetIncomeMultiplier(); }
     	energy += amount;
     	energyIncome += amount;
     	if (energy > energyStorage) {
    -		delayedEnergyShare += (energy - energyStorage);
    +		delayedEnergyShare += energy - energyStorage;
     		energy = energyStorage;
     	}
     }
     
    -
    -
     void CTeam::GiveEverythingTo(const unsigned toTeam)
     {
     	CTeam* target = teamHandler->Team(toTeam);
    @@ -220,6 +253,10 @@ void CTeam::Died()
     	eventHandler.TeamDied(teamNum);
     }
     
    +void CTeam::StartposMessage(const float3& pos)
    +{
    +	startPos = pos;
    +}
     
     CTeam& CTeam::operator=(const TeamBase& base)
     {
    @@ -227,36 +264,43 @@ CTeam& CTeam::operator=(const TeamBase& base)
     	return *this;
     }
     
    -
    -
    -void CTeam::ResetResourceState()
    +void CTeam::ResetFrameVariables()
     {
    -	// reset all state variables that were
    -	// potentially modified during the last
    -	// <TEAM_SLOWUPDATE_RATE> frames
    -	prevMetalPull     = metalPull;     metalPull     = 0.0f;
    -	prevMetalIncome   = metalIncome;   metalIncome   = 0.0f;
    -	prevMetalExpense  = metalExpense;  metalExpense  = 0.0f;
    -	prevEnergyPull    = energyPull;    energyPull    = 0.0f;
    -	prevEnergyIncome  = energyIncome;  energyIncome  = 0.0f;
    -	prevEnergyExpense = energyExpense; energyExpense = 0.0f;
    -
    -	// reset the sharing accumulators
    -	metalSent = 0.0f; metalReceived = 0.0f;
    -	energySent = 0.0f; energyReceived = 0.0f;
    +	prevMetalPull     = metalPull;
    +	prevMetalIncome   = metalIncome;
    +	prevMetalExpense  = metalExpense;
    +	prevMetalUpkeep   = metalUpkeep;
    +	prevEnergyPull    = energyPull;
    +	prevEnergyIncome  = energyIncome;
    +	prevEnergyExpense = energyExpense;
    +	prevEnergyUpkeep  = energyUpkeep;
    +
    +	metalPull = 0;
    +	metalIncome = 0;
    +	metalExpense = 0;
    +	metalUpkeep = 0;
    +	energyPull = 0;
    +	energyIncome = 0;
    +	energyExpense = 0;
    +	energyUpkeep = 0;
    +
    +	metalSent = 0;
    +	energySent = 0;
    +	metalReceived = 0;
    +	energyReceived = 0;
     }
     
     void CTeam::SlowUpdate()
     {
    -	float eShare = 0.0f, mShare = 0.0f;
    +	currentStats->metalProduced  += prevMetalIncome;
    +	currentStats->energyProduced += prevEnergyIncome;
    +	currentStats->metalUsed  += prevMetalUpkeep + prevMetalExpense;
    +	currentStats->energyUsed += prevEnergyUpkeep + prevEnergyExpense;
     
    -	// calculate the total amount of resources that all
    -	// (allied) teams can collectively receive through
    -	// sharing
    +	float eShare = 0.0f, mShare = 0.0f;
     	for (int a = 0; a < teamHandler->ActiveTeams(); ++a) {
    -		CTeam* team = teamHandler->Team(a);
    -
     		if ((a != teamNum) && (teamHandler->AllyTeam(teamNum) == teamHandler->AllyTeam(a))) {
    +			CTeam* team = teamHandler->Team(a);
     			if (team->isDead)
     				continue;
     
    @@ -265,25 +309,21 @@ void CTeam::SlowUpdate()
     		}
     	}
     
    +	metal += delayedMetalShare;
    +	energy += delayedEnergyShare;
    +	delayedMetalShare = 0;
    +	delayedEnergyShare = 0;
     
    -	currentStats->metalProduced  += prevMetalIncome;
    -	currentStats->energyProduced += prevEnergyIncome;
    -	currentStats->metalUsed  += prevMetalExpense;
    -	currentStats->energyUsed += prevEnergyExpense;
    -
    -	metal  += delayedMetalShare;  delayedMetalShare  = 0.0f;
    -	energy += delayedEnergyShare; delayedEnergyShare = 0.0f;
    -
    -
    -	// calculate how much we can share in total (any and all excess resources)
     	const float eExcess = std::max(0.0f, energy - (energyStorage * energyShare));
     	const float mExcess = std::max(0.0f, metal  - (metalStorage  * metalShare));
     
     	float de = 0.0f, dm = 0.0f;
    -	if (eShare > 0.0f) { de = std::min(1.0f, eExcess / eShare); }
    -	if (mShare > 0.0f) { dm = std::min(1.0f, mExcess / mShare); }
    -
    -	// now evenly distribute our excess resources among allied teams
    +	if (eShare > 0.0f) {
    +		de = std::min(1.0f, eExcess/eShare);
    +	}
    +	if (mShare > 0.0f) {
    +		dm = std::min(1.0f, mExcess/mShare);
    +	}
     	for (int a = 0; a < teamHandler->ActiveTeams(); ++a) {
     		if ((a != teamNum) && (teamHandler->AllyTeam(teamNum) == teamHandler->AllyTeam(a))) {
     			CTeam* team = teamHandler->Team(a);
    @@ -291,19 +331,23 @@ void CTeam::SlowUpdate()
     				continue;
     
     			const float edif = std::max(0.0f, (team->energyStorage * 0.99f) - team->energy) * de;
    -			const float mdif = std::max(0.0f, (team->metalStorage * 0.99f) - team->metal) * dm;
    +			energy -= edif;
    +			energySent += edif;
    +			currentStats->energySent += edif;
    +			team->energy += edif;
    +			team->energyReceived += edif;
    +			team->currentStats->energyReceived += edif;
     
    -			energy     -= edif; team->energy         += edif;
    -			energySent += edif; team->energyReceived += edif;
    -			metal      -= mdif; team->metal          += mdif;
    -			metalSent  += mdif; team->metalReceived  += mdif;
    -
    -			currentStats->energySent += edif; team->currentStats->energyReceived += edif;
    -			currentStats->metalSent  += mdif; team->currentStats->metalReceived  += mdif;
    +			const float mdif = std::max(0.0f, (team->metalStorage * 0.99f) - team->metal) * dm;
    +			metal -= mdif;
    +			metalSent += mdif;
    +			currentStats->metalSent += mdif;
    +			team->metal += mdif;
    +			team->metalReceived += mdif;
    +			team->currentStats->metalReceived += mdif;
     		}
     	}
     
    -	// clamp resource levels to storage capacity
     	if (metal > metalStorage) {
     		currentStats->metalExcess += (metal - metalStorage);
     		metal = metalStorage;
    @@ -325,10 +369,11 @@ void CTeam::SlowUpdate()
     		nextHistoryEntry = gs->frameNum + statsFrames;
     		currentStats->frame = nextHistoryEntry;
     	}
    +
     }
     
     
    -void CTeam::AddUnit(CUnit* unit, AddType type)
    +void CTeam::AddUnit(CUnit* unit,AddType type)
     {
     	units.insert(unit);
     	switch (type) {
    @@ -348,7 +393,7 @@ void CTeam::AddUnit(CUnit* unit, AddType type)
     }
     
     
    -void CTeam::RemoveUnit(CUnit* unit, RemoveType type)
    +void CTeam::RemoveUnit(CUnit* unit,RemoveType type)
     {
     	units.erase(unit);
     	switch (type) {
    diff --git a/rts/Sim/Misc/Team.h b/rts/Sim/Misc/Team.h
    index fdd6bcf..f390cbc 100644
    --- a/rts/Sim/Misc/Team.h
    +++ b/rts/Sim/Misc/Team.h
    @@ -21,20 +21,28 @@ class CTeam : public TeamBase, private boost::noncopyable //! cannot allow shall
     	CR_DECLARE(CTeam);
     public:
     	CTeam();
    +	~CTeam();
    +public:
     
    -	void ResetResourceState();
    +	/**
    +	 * This has to be called for every team before SlowUpdates start,
    +	 * otherwise values get overwritten.
    +	 */
    +	void ResetFrameVariables();
     	void SlowUpdate();
     
     	void AddMetal(float amount, bool useIncomeMultiplier = true);
     	void AddEnergy(float amount, bool useIncomeMultiplier = true);
     	bool UseEnergy(float amount);
     	bool UseMetal(float amount);
    +	bool UseEnergyUpkeep(float amount);
    +	bool UseMetalUpkeep(float amount);
     
     	void GiveEverythingTo(const unsigned toTeam);
     
     	void Died();
     
    -	void StartposMessage(const float3& pos) { startPos = pos; }
    +	void StartposMessage(const float3& pos);
     
     	CTeam& operator=(const TeamBase& base);
     
    @@ -71,10 +79,12 @@ public:
     	float metalPull,    prevMetalPull;
     	float metalIncome,  prevMetalIncome;
     	float metalExpense, prevMetalExpense;
    +	float metalUpkeep,  prevMetalUpkeep;
     
     	float energyPull,    prevEnergyPull;
     	float energyIncome,  prevEnergyIncome;
     	float energyExpense, prevEnergyExpense;
    +	float energyUpkeep,  prevEnergyUpkeep;
     
     	SyncedFloat metalStorage, energyStorage;
     
    diff --git a/rts/Sim/Misc/TeamHandler.cpp b/rts/Sim/Misc/TeamHandler.cpp
    index 0bbbf99..04e5bd0 100644
    --- a/rts/Sim/Misc/TeamHandler.cpp
    +++ b/rts/Sim/Misc/TeamHandler.cpp
    @@ -25,7 +25,7 @@ CR_REG_METADATA(CTeamHandler, (
     ));
     
     
    -CTeamHandler* teamHandler = NULL;
    +CTeamHandler* teamHandler;
     
     
     CTeamHandler::CTeamHandler():
    @@ -88,12 +88,12 @@ void CTeamHandler::LoadFromSetup(const CGameSetup* setup)
     
     void CTeamHandler::GameFrame(int frameNum)
     {
    -	if ((frameNum % TEAM_SLOWUPDATE_RATE) == 0) {
    +	if (!(frameNum & (TEAM_SLOWUPDATE_RATE-1))) {
     		for (int a = 0; a < ActiveTeams(); ++a) {
    -			teams[a]->ResetResourceState();
    +			Team(a)->ResetFrameVariables();
     		}
     		for (int a = 0; a < ActiveTeams(); ++a) {
    -			teams[a]->SlowUpdate();
    +			Team(a)->SlowUpdate();
     		}
     	}
     }
    diff --git a/rts/Sim/MoveTypes/AAirMoveType.cpp b/rts/Sim/MoveTypes/AAirMoveType.cpp
    index 5cabd4e..67943ce 100644
    --- a/rts/Sim/MoveTypes/AAirMoveType.cpp
    +++ b/rts/Sim/MoveTypes/AAirMoveType.cpp
    @@ -39,9 +39,9 @@ AAirMoveType::AAirMoveType(CUnit* unit) :
     	wantedHeight(80.0f),
     	collide(true),
     	useSmoothMesh(false),
    -	autoLand(true),
     	lastColWarning(NULL),
     	lastColWarningType(0),
    +	autoLand(true),
     	lastFuelUpdateFrame(0)
     {
     	useHeading = false;
    diff --git a/rts/Sim/MoveTypes/AirMoveType.cpp b/rts/Sim/MoveTypes/AirMoveType.cpp
    index f0aade2..bc09cbf 100644
    --- a/rts/Sim/MoveTypes/AirMoveType.cpp
    +++ b/rts/Sim/MoveTypes/AirMoveType.cpp
    @@ -1088,7 +1088,7 @@ void CAirMoveType::SetState(AAirMoveType::AircraftState state)
     
     
     
    -void CAirMoveType::ImpulseAdded(const float3&)
    +void CAirMoveType::ImpulseAdded(void)
     {
     	if (aircraftState == AIRCRAFT_FLYING) {
     		owner->speed += owner->residualImpulse;
    diff --git a/rts/Sim/MoveTypes/AirMoveType.h b/rts/Sim/MoveTypes/AirMoveType.h
    index 2f70cab..bc3db18 100644
    --- a/rts/Sim/MoveTypes/AirMoveType.h
    +++ b/rts/Sim/MoveTypes/AirMoveType.h
    @@ -32,7 +32,7 @@ public:
     			float engine, const float3& engineVector);
     	void SetState(AircraftState state);
     	void UpdateTakeOff(float wantedHeight);
    -	void ImpulseAdded(const float3&);
    +	void ImpulseAdded();
     	float3 FindLandingPos() const;
     
     	void DependentDied(CObject* o);
    diff --git a/rts/Sim/MoveTypes/GroundMoveType.cpp b/rts/Sim/MoveTypes/GroundMoveType.cpp
    index 58e8670..984aa85 100644
    --- a/rts/Sim/MoveTypes/GroundMoveType.cpp
    +++ b/rts/Sim/MoveTypes/GroundMoveType.cpp
    @@ -81,11 +81,12 @@ CR_REG_METADATA(CGroundMoveType, (
     	CR_MEMBER(canReverse),
     	CR_MEMBER(useMainHeading),
     
    +	CR_MEMBER(skidRotSpeed),
    +
     	CR_MEMBER(waypointDir),
     	CR_MEMBER(flatFrontDir),
     
     	CR_MEMBER(skidRotVector),
    -	CR_MEMBER(skidRotSpeed),
     	CR_MEMBER(skidRotSpeed2),
     	CR_MEMBER(skidRotPos2),
     	CR_ENUM_MEMBER(oldPhysState),
    @@ -110,10 +111,10 @@ CGroundMoveType::CGroundMoveType(CUnit* owner):
     	maxReverseSpeed(0.0f),
     	wantedSpeed(0.0f),
     	currentSpeed(0.0f),
    -	requestedSpeed(0.0f),
     	deltaSpeed(0.0f),
     	deltaHeading(0),
     
    +	flatFrontDir(0.0f, 0.0, 1.0f),
     	pathId(0),
     	goalRadius(0),
     
    @@ -121,7 +122,15 @@ CGroundMoveType::CGroundMoveType(CUnit* owner):
     	nextWaypoint(ZeroVector),
     	atGoal(false),
     	haveFinalWaypoint(false),
    +
    +	requestedSpeed(0.0f),
     	currentDistanceToWaypoint(0),
    +	pathRequestDelay(0),
    +	numIdlingUpdates(0),
    +	numIdlingSlowUpdates(0),
    +
    +	nextDeltaSpeedUpdate(0),
    +	nextObstacleAvoidanceUpdate(0),
     
     	skidding(false),
     	flying(false),
    @@ -130,22 +139,12 @@ CGroundMoveType::CGroundMoveType(CUnit* owner):
     	canReverse(owner->unitDef->rSpeed > 0.0f),
     	useMainHeading(false),
     
    -	skidRotVector(UpVector),
     	skidRotSpeed(0.0f),
    +	skidRotVector(UpVector),
     	skidRotSpeed2(0.0f),
     	skidRotPos2(0.0f),
     	oldPhysState(CSolidObject::OnGround),
    -
    -	flatFrontDir(0.0f, 0.0, 1.0f),
    -	mainHeadingPos(ZeroVector),
    -
    -	nextDeltaSpeedUpdate(0),
    -	nextObstacleAvoidanceUpdate(0),
    -
    -	pathRequestDelay(0),
    -
    -	numIdlingUpdates(0),
    -	numIdlingSlowUpdates(0)
    +	mainHeadingPos(0.0f, 0.0f, 0.0f)
     {
     	if (owner) {
     		moveSquareX = owner->pos.x / MIN_WAYPOINT_DISTANCE;
    @@ -560,17 +559,23 @@ void CGroundMoveType::ChangeHeading(short wantedHeading) {
     	tracefile << "unit " << owner->id << " changed heading to " << heading << " from " << _oldheading << " (wantedHeading: " << wantedHeading << ")\n";
     #endif
     
    -	owner->SetDirectionFromHeading();
    +	owner->frontdir = GetVectorFromHeading(heading);
    +	if (owner->upright) {
    +		owner->updir = UpVector;
    +		owner->rightdir = owner->frontdir.cross(owner->updir);
    +	} else {
    +		owner->updir = ground->GetNormal(owner->pos.x, owner->pos.z);
    +		owner->rightdir = owner->frontdir.cross(owner->updir);
    +		owner->rightdir.Normalize();
    +		owner->frontdir = owner->updir.cross(owner->rightdir);
    +	}
     
     	flatFrontDir = owner->frontdir;
     	flatFrontDir.y = 0.0f;
     	flatFrontDir.Normalize();
     }
     
    -
    -
    -
    -void CGroundMoveType::ImpulseAdded(const float3&)
    +void CGroundMoveType::ImpulseAdded()
     {
     	if (owner->beingBuilt || owner->unitDef->movedata->moveType == MoveData::Ship_Move)
     		return;
    @@ -583,34 +588,25 @@ void CGroundMoveType::ImpulseAdded(const float3&)
     		impulse = ZeroVector;
     	}
     
    -	const float3& groundNormal = ground->GetNormal(owner->pos.x, owner->pos.z);
    -	const float groundImpulseScale = impulse.dot(groundNormal);
    +	float3 groundNormal = ground->GetNormal(owner->pos.x, owner->pos.z);
    +
    +	if (impulse.dot(groundNormal) < 0)
    +		impulse -= groundNormal * impulse.dot(groundNormal);
     
    -	if (groundImpulseScale < 0.0f)
    -		impulse -= (groundNormal * groundImpulseScale);
    +	const float sqstrength = impulse.SqLength();
     
    -	if (impulse.SqLength() > 9.0f || groundImpulseScale > 0.3f) {
    +	if (sqstrength > 9 || impulse.dot(groundNormal) > 0.3f) {
     		skidding = true;
     		speed += impulse;
     		impulse = ZeroVector;
     
    -		// FIXME: impulse should not cause a _random_ rotational component
     		skidRotSpeed += (gs->randFloat() - 0.5f) * 1500;
    -		skidRotSpeed2 = 0.0f;
    -		skidRotPos2 = 0.0f;
    -
    -		float3 skidDir;
    -
    -		if (speed.SqLength2D() >= 0.01f) {
    -			skidDir = speed;
    -			skidDir.y = 0.0f;
    +		skidRotPos2 = 0;
    +		skidRotSpeed2 = 0;
    +		float3 skidDir(speed);
    +			skidDir.y = 0;
     			skidDir.Normalize();
    -		} else {
    -			skidDir = owner->frontdir;
    -		}
    -
     		skidRotVector = skidDir.cross(UpVector);
    -
     		oldPhysState = owner->physicalState;
     		owner->physicalState = CSolidObject::Flying;
     		owner->moveType->useHeading = false;
    @@ -690,8 +686,8 @@ void CGroundMoveType::UpdateSkid()
     				speed *= (speedf - speedReduction) / speedf;
     			}
     
    -			const float remTime = speedf / speedReduction - 1.0f;
    -			const float rp = floor(skidRotPos2 + skidRotSpeed2 * remTime + 0.5f);
    +			float remTime = speedf / speedReduction - 1.0f;
    +			float rp = floor(skidRotPos2 + skidRotSpeed2 * remTime + 0.5f);
     
     			skidRotSpeed2 = (remTime + 1.0f == 0.0f ) ? 0.0f : (rp - skidRotPos2) / (remTime + 1.0f);
     
    @@ -720,7 +716,13 @@ void CGroundMoveType::UpdateSkid()
     		}
     	}
     	CalcSkidRot();
    -	owner->MoveMidPos(speed);
    +
    +	midPos += speed;
    +	pos = midPos -
    +		owner->frontdir * owner->relMidPos.z -
    +		owner->updir    * owner->relMidPos.y -
    +		owner->rightdir * owner->relMidPos.x;
    +	owner->midPos = midPos;
     
     	CheckCollisionSkid();
     	ASSERT_SYNCED_FLOAT3(owner->midPos);
    @@ -733,17 +735,25 @@ void CGroundMoveType::UpdateControlledDrop()
     	SyncedFloat3& midPos = owner->midPos;
     
     	if (owner->falling) {
    -		speed.y += (mapInfo->map.gravity * owner->fallSpeed);
    -		speed.y = std::min(speed.y, 0.0f);
    +		speed.y += mapInfo->map.gravity * owner->fallSpeed;
     
    -		owner->MoveMidPos(speed);
    +		if (owner->speed.y > 0) //sometimes the dropped unit gets an upward force, still unsure where its coming from
    +			owner->speed.y = 0;
     
    -		if (midPos.y < 0.0f)
    -			speed *= 0.90;
    +		midPos += speed;
    +		pos = midPos -
    +			owner->frontdir * owner->relMidPos.z -
    +			owner->updir * owner->relMidPos.y -
    +			owner->rightdir * owner->relMidPos.x;
    +
    +		owner->midPos.y = owner->pos.y + owner->relMidPos.y;
    +
    +		if(midPos.y < 0)
    +			speed*=0.90;
     
     		const float wh = GetGroundHeight(midPos);
     
    -		if (wh > (midPos.y - owner->relMidPos.y)) {
    +		if (wh > midPos.y - owner->relMidPos.y) {
     			owner->falling = false;
     			midPos.y = wh + owner->relMidPos.y - speed.y * 0.8;
     			owner->script->Landed(); //stop parachute animation
    @@ -781,8 +791,12 @@ void CGroundMoveType::CheckCollisionSkid()
     			const float impactSpeed = -owner->speed.dot(dif);
     
     			if (impactSpeed > 0.0f) {
    -				owner->MoveMidPos(dif * impactSpeed);
    -				owner->speed += ((dif * impactSpeed) * 1.8f);
    +				midPos += dif * impactSpeed;
    +				pos = midPos -
    +					owner->frontdir * owner->relMidPos.z -
    +					owner->updir    * owner->relMidPos.y -
    +					owner->rightdir * owner->relMidPos.x;
    +				owner->speed += dif * (impactSpeed * 1.8f);
     
     				// damage the collider
     				if (impactSpeed > ownerUD->minCollisionSpeed && ownerUD->minCollisionSpeed >= 0) {
    @@ -799,10 +813,17 @@ void CGroundMoveType::CheckCollisionSkid()
     			const float impactSpeed = (u->speed - owner->speed).dot(dif) * 0.5f;
     
     			if (impactSpeed > 0.0f) {
    -				owner->MoveMidPos(dif * (impactSpeed * (1 - part) * 2));
    +				midPos += dif * (impactSpeed * (1 - part) * 2);
    +				pos = midPos -
    +					owner->frontdir * owner->relMidPos.z -
    +					owner->updir    * owner->relMidPos.y -
    +					owner->rightdir * owner->relMidPos.x;
     				owner->speed += dif * (impactSpeed * (1 - part) * 2);
    -
    -				u->MoveMidPos(dif * (impactSpeed * part * -2));
    +				u->midPos -= dif * (impactSpeed * part * 2);
    +				u->pos = u->midPos -
    +					u->frontdir * u->relMidPos.z -
    +					u->updir    * u->relMidPos.y -
    +					u->rightdir * u->relMidPos.x;
     				u->speed -= dif * (impactSpeed * part * 2);
     
     				if (CGroundMoveType* mt = dynamic_cast<CGroundMoveType*>(u->moveType)) {
    @@ -845,8 +866,12 @@ void CGroundMoveType::CheckCollisionSkid()
     		const float impactSpeed = -owner->speed.dot(dif);
     
     		if (impactSpeed > 0.0f) {
    -			owner->MoveMidPos(dif * impactSpeed);
    -			owner->speed += ((dif * impactSpeed) * 1.8f);
    +			midPos += dif * impactSpeed;
    +			pos = midPos -
    +				owner->frontdir * owner->relMidPos.z -
    +				owner->updir    * owner->relMidPos.y -
    +				owner->rightdir * owner->relMidPos.x;
    +			owner->speed += dif * (impactSpeed * 1.8f);
     
     			if (impactSpeed > ownerUD->minCollisionSpeed && ownerUD->minCollisionSpeed >= 0) {
     				owner->DoDamage(DamageArray(impactSpeed * owner->mass * 0.2f), 0, ZeroVector);
    @@ -859,7 +884,18 @@ void CGroundMoveType::CheckCollisionSkid()
     void CGroundMoveType::CalcSkidRot()
     {
     	owner->heading += (short int) skidRotSpeed;
    -	owner->SetDirectionFromHeading();
    +
    +	owner->frontdir = GetVectorFromHeading(owner->heading);
    +
    +	if (owner->upright) {
    +		owner->updir = UpVector;
    +		owner->rightdir = owner->frontdir.cross(owner->updir);
    +	} else {
    +		owner->updir = ground->GetSmoothNormal(owner->pos.x, owner->pos.z);
    +		owner->rightdir = owner->frontdir.cross(owner->updir);
    +		owner->rightdir.Normalize();
    +		owner->frontdir = owner->updir.cross(owner->rightdir);
    +	}
     
     	skidRotPos2 += skidRotSpeed2;
     
    @@ -868,25 +904,22 @@ void CGroundMoveType::CalcSkidRot()
     
     	float3 f1 = skidRotVector * skidRotVector.dot(owner->frontdir);
     	float3 f2 = owner->frontdir - f1;
    +	f2 = f2 * cosp + f2.cross(skidRotVector) * sinp;
    +	owner->frontdir = f1 + f2;
     
     	float3 r1 = skidRotVector * skidRotVector.dot(owner->rightdir);
     	float3 r2 = owner->rightdir - r1;
    +	r2 = r2 * cosp + r2.cross(skidRotVector) * sinp;
    +	owner->rightdir = r1 + r2;
     
     	float3 u1 = skidRotVector * skidRotVector.dot(owner->updir);
     	float3 u2 = owner->updir - u1;
    -
    -	f2 = f2 * cosp + f2.cross(skidRotVector) * sinp;
    -	r2 = r2 * cosp + r2.cross(skidRotVector) * sinp;
     	u2 = u2 * cosp + u2.cross(skidRotVector) * sinp;
    -
    -	owner->frontdir = f1 + f2;
    -	owner->rightdir = r1 + r2;
    -	owner->updir    = u1 + u2;
    +	owner->updir = u1 + u2;
     }
     
     
     
    -
     /*
      * Dynamic obstacle avoidance, helps the unit to
      * follow the path even when it's not perfect.
    diff --git a/rts/Sim/MoveTypes/GroundMoveType.h b/rts/Sim/MoveTypes/GroundMoveType.h
    index 30ee478..ad8c805 100644
    --- a/rts/Sim/MoveTypes/GroundMoveType.h
    +++ b/rts/Sim/MoveTypes/GroundMoveType.h
    @@ -29,7 +29,7 @@ public:
     
     	void SetMaxSpeed(float speed);
     
    -	void ImpulseAdded(const float3&);
    +	void ImpulseAdded();
     
     	void KeepPointingTo(float3 pos, float distance, bool aggressive);
     	void KeepPointingTo(CUnit* unit, float distance, bool aggressive);
    diff --git a/rts/Sim/MoveTypes/MoveType.h b/rts/Sim/MoveTypes/MoveType.h
    index b89ff88..c47428c 100644
    --- a/rts/Sim/MoveTypes/MoveType.h
    +++ b/rts/Sim/MoveTypes/MoveType.h
    @@ -23,7 +23,7 @@ public:
     	virtual void KeepPointingTo(float3 pos, float distance, bool aggressive) = 0;
     	virtual void KeepPointingTo(CUnit* unit, float distance, bool aggressive);
     	virtual void StopMoving() = 0;
    -	virtual void ImpulseAdded(const float3&) {}
    +	virtual void ImpulseAdded(void) {}
     	virtual void ReservePad(CAirBaseHandler::LandingPad* lp);
     
     	virtual void SetGoal(const float3& pos);
    diff --git a/rts/Sim/MoveTypes/ScriptMoveType.h b/rts/Sim/MoveTypes/ScriptMoveType.h
    index 1195710..904fd91 100644
    --- a/rts/Sim/MoveTypes/ScriptMoveType.h
    +++ b/rts/Sim/MoveTypes/ScriptMoveType.h
    @@ -34,6 +34,7 @@ class CScriptMoveType : public AMoveType
     		void KeepPointingTo(CUnit* unit, float distance, bool aggressive) {}
     		void StopMoving() {}
     
    +		void ImpulseAdded() {}
     		void SetGoal(float3 pos) {}
     		void SetMaxSpeed(float speed) {}
     		void SetWantedMaxSpeed(float speed) {}
    diff --git a/rts/Sim/Objects/SolidObject.cpp b/rts/Sim/Objects/SolidObject.cpp
    index f3ec270..949f734 100644
    --- a/rts/Sim/Objects/SolidObject.cpp
    +++ b/rts/Sim/Objects/SolidObject.cpp
    @@ -60,8 +60,8 @@ CSolidObject::CSolidObject():
     	isMoving(false),
     	isUnderWater(false),
     	isMarkedOnBlockingMap(false),
    -	speed(ZeroVector),
    -	residualImpulse(ZeroVector),
    +	speed(0, 0, 0),
    +	residualImpulse(0, 0, 0),
     	allyteam(0),
     	team(0),
     	mobility(NULL),
    diff --git a/rts/Sim/Path/Default/PathEstimator.cpp b/rts/Sim/Path/Default/PathEstimator.cpp
    index b612a2b..e64fb78 100644
    --- a/rts/Sim/Path/Default/PathEstimator.cpp
    +++ b/rts/Sim/Path/Default/PathEstimator.cpp
    @@ -1,9 +1,6 @@
     /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
     
     #include "StdAfx.h"
    -
    -#include "lib/gml/gml.h" // FIXME: linux for some reason does not compile without this
    -
     #include "PathEstimator.h"
     
     #include <fstream>
    diff --git a/rts/Sim/Projectiles/Projectile.h b/rts/Sim/Projectiles/Projectile.h
    index ab75359..b0385c1 100644
    --- a/rts/Sim/Projectiles/Projectile.h
    +++ b/rts/Sim/Projectiles/Projectile.h
    @@ -3,7 +3,7 @@
     #ifndef PROJECTILE_H
     #define PROJECTILE_H
     
    -#include "lib/gml/gml.h" // for GML_ENABLE_SIM
    +#include "Rendering/GL/myGL.h"
     
     #ifdef _MSC_VER
     #pragma warning(disable:4291)
    diff --git a/rts/Sim/Units/Unit.cpp b/rts/Sim/Units/Unit.cpp
    index 7802b41..07ac36a 100644
    --- a/rts/Sim/Units/Unit.cpp
    +++ b/rts/Sim/Units/Unit.cpp
    @@ -120,7 +120,6 @@ CUnit::CUnit() : CSolidObject(),
     	stunned(false),
     	useHighTrajectory(false),
     	dontUseWeapons(false),
    -	dontFire(false),
     	deathScriptFinished(false),
     	delayedWreckLevel(-1),
     	restTime(0),
    @@ -201,6 +200,7 @@ CUnit::CUnit() : CSolidObject(),
     	commandShotCount(-1),
     	fireState(FIRESTATE_FIREATWILL),
     	moveState(MOVESTATE_MANEUVER),
    +	dontFire(false),
     	activated(false),
     	crashing(false),
     	isDead(false),
    @@ -233,10 +233,6 @@ CUnit::CUnit() : CSolidObject(),
     	myTrack(NULL),
     	lastFlareDrop(0),
     	currentFuel(0.0f),
    -	maxSpeed(0.0f),
    -	maxReverseSpeed(0.0f),
    -	alphaThreshold(0.1f),
    -	cegDamage(1),
     	luaDraw(false),
     	noDraw(false),
     	noSelect(false),
    @@ -244,8 +240,12 @@ CUnit::CUnit() : CSolidObject(),
     	leaveTracks(false),
     	isIcon(false),
     	iconRadius(0.0f),
    +	maxSpeed(0.0f),
    +	maxReverseSpeed(0.0f),
     	lodCount(0),
    -	currentLOD(0)
    +	currentLOD(0),
    +	alphaThreshold(0.1f),
    +	cegDamage(1)
     #ifdef USE_GML
     	, lastDrawFrame(-30)
     #endif
    @@ -593,6 +593,45 @@ void CUnit::ForcedSpin(const float3& newDir)
     	ForcedMove(pos); // lazy, don't need to update the quadfield, etc...
     }
     
    +
    +/*
    +void CUnit::SetFront(const SyncedFloat3& newDir)
    +{
    +	frontdir = newDir;
    +	frontdir.Normalize();
    +	rightdir = frontdir.cross(updir);
    +	rightdir.Normalize();
    +	updir = rightdir.cross(frontdir);
    +	updir.Normalize();
    +	heading = GetHeadingFromVector(frontdir.x, frontdir.z);
    +	UpdateMidPos();
    +}
    +
    +void CUnit::SetUp(const SyncedFloat3& newDir)
    +{
    +	updir = newDir;
    +	updir.Normalize();
    +	frontdir = updir.cross(rightdir);
    +	frontdir.Normalize();
    +	rightdir = frontdir.cross(updir);
    +	rightdir.Normalize();
    +	heading = GetHeadingFromVector(frontdir.x, frontdir.z);
    +	UpdateMidPos();
    +}
    +
    +void CUnit::SetRight(const SyncedFloat3& newDir)
    +{
    +	rightdir = newDir;
    +	rightdir.Normalize();
    +	updir = rightdir.cross(frontdir);
    +	updir.Normalize();
    +	frontdir = updir.cross(rightdir);
    +	frontdir.Normalize();
    +	heading = GetHeadingFromVector(frontdir.x, frontdir.z);
    +	UpdateMidPos();
    +}
    +*/
    +
     void CUnit::SetDirectionFromHeading()
     {
     	if (GetTransporter() == NULL) {
    @@ -619,14 +658,6 @@ void CUnit::UpdateMidPos()
     		(rightdir * relMidPos.x);
     }
     
    -void CUnit::MoveMidPos(const float3& deltaPos) {
    -	midPos += deltaPos;
    -	pos = midPos -
    -		(frontdir * relMidPos.z) -
    -		(updir    * relMidPos.y) -
    -		(rightdir * relMidPos.x);
    -}
    -
     
     void CUnit::Drop(const float3& parentPos, const float3& parentDir, CUnit* parent)
     {
    @@ -1054,21 +1085,18 @@ void CUnit::SlowUpdateWeapons() {
     
     void CUnit::DoWaterDamage()
     {
    -	if (mapInfo->water.damage <= 0.0f) {
    -		return;
    -	}
    -	if (!pos.IsInBounds()) {
    -		return;
    -	}
    -
    -	const int  px            = pos.x / (SQUARE_SIZE * 2);
    -	const int  pz            = pos.z / (SQUARE_SIZE * 2);
    -	const bool isFloating    = (physicalState == CSolidObject::Floating);
    -	const bool onGround      = (physicalState == CSolidObject::OnGround);
    -	const bool isWaterSquare = (readmap->mipHeightmap[1][pz * gs->hmapx + px] <= 0.0f);
    -
    -	if ((pos.y <= 0.0f) && isWaterSquare && (isFloating || onGround)) {
    -		DoDamage(DamageArray(mapInfo->water.damage), 0, ZeroVector, -1);
    +	if (uh->waterDamage > 0.0f) {
    +		if (pos.IsInBounds()) {
    +			const int  px            = int(pos.x / (SQUARE_SIZE * 2));
    +			const int  pz            = int(pos.z / (SQUARE_SIZE * 2));
    +			const bool isFloating    = (physicalState == CSolidObject::Floating);
    +			const bool onGround      = (physicalState == CSolidObject::OnGround);
    +			const bool isWaterSquare = (readmap->mipHeightmap[1][pz * gs->hmapx + px] <= 0.0f);
    +
    +			if ((pos.y <= 0.0f) && isWaterSquare && (isFloating || onGround)) {
    +				DoDamage(DamageArray(uh->waterDamage), 0, ZeroVector, -1);
    +			}
    +		}
     	}
     }
     
    @@ -1128,7 +1156,8 @@ void CUnit::DoDamage(const DamageArray& damages, CUnit* attacker, const float3&
     		damage = newDamage;
     	}
     
    -	AddImpulse((impulse * impulseMult) / mass);
    +	residualImpulse += ((impulse * impulseMult) / mass);
    +	moveType->ImpulseAdded();
     
     	if (paralyzeTime == 0) { // real damage
     		if (damage > 0.0f) {
    @@ -1149,7 +1178,8 @@ void CUnit::DoDamage(const DamageArray& damages, CUnit* attacker, const float3&
     				stunned = false;
     			}
     		}
    -	} else { // paralyzation
    +	}
    +	else { // paralyzation
     		experienceMod *= 0.1f; // reduced experience
     		if (damage > 0.0f) {
     			// paralyzeDamage may not get higher than maxHealth * (paralyzeTime + 1),
    @@ -1223,13 +1253,6 @@ void CUnit::Kill(const float3& impulse) {
     	DoDamage(da, NULL, impulse, -1);
     }
     
    -void CUnit::AddImpulse(const float3& addedImpulse) {
    -	residualImpulse += addedImpulse;
    -
    -	if (addedImpulse.SqLength() >= 0.01f)
    -		moveType->ImpulseAdded(addedImpulse);
    -}
    -
     
     
     /******************************************************************************/
    diff --git a/rts/Sim/Units/Unit.h b/rts/Sim/Units/Unit.h
    index 3aa74a0..31871cb 100644
    --- a/rts/Sim/Units/Unit.h
    +++ b/rts/Sim/Units/Unit.h
    @@ -3,8 +3,6 @@
     #ifndef UNIT_H
     #define UNIT_H
     
    -#include "lib/gml/gml.h" // for GML_ENABLE_SIM
    -
     #include <map>
     #include <vector>
     #include <string>
    @@ -82,7 +80,6 @@ public:
     	                      const float3& impulse, int weaponId = -1);
     	virtual void DoWaterDamage();
     	virtual void Kill(const float3& impulse);
    -	virtual void AddImpulse(const float3&);
     	virtual void FinishedBuilding();
     
     	bool AttackGround(const float3& pos, bool wantDGun, bool fpsMode = false);
    @@ -150,7 +147,6 @@ public:
     	void UpdateTerrainType();
     
     	void UpdateMidPos();
    -	void MoveMidPos(const float3&);
     
     	bool IsNeutral() const {
     		return neutral;
    diff --git a/rts/Sim/Units/UnitHandler.cpp b/rts/Sim/Units/UnitHandler.cpp
    index 2c8326e..ab14579 100644
    --- a/rts/Sim/Units/UnitHandler.cpp
    +++ b/rts/Sim/Units/UnitHandler.cpp
    @@ -1,18 +1,20 @@
     /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
     
     #include "StdAfx.h"
    -#include <cassert>
    +#include <assert.h>
     #include "mmgr.h"
     
     #include "lib/gml/gml.h"
     #include "UnitHandler.h"
     #include "Unit.h"
     #include "UnitDefHandler.h"
    -#include "CommandAI/BuilderCAI.h"
    +#include "UnitLoader.h"
     #include "CommandAI/Command.h"
    +#include "CommandAI/BuilderCAI.h"
     #include "Game/GameSetup.h"
     #include "Lua/LuaUnsyncedCtrl.h"
     #include "Map/Ground.h"
    +#include "Map/MapInfo.h"
     #include "Map/ReadMap.h"
     #include "Sim/Features/Feature.h"
     #include "Sim/Features/FeatureDef.h"
    @@ -27,6 +29,7 @@
     #include "System/LogOutput.h"
     #include "System/TimeProfiler.h"
     #include "System/myMath.h"
    +#include "System/LoadSave/LoadSaveInterface.h"
     #include "System/Sync/SyncTracer.h"
     #include "System/creg/STL_Deque.h"
     #include "System/creg/STL_List.h"
    @@ -39,24 +42,29 @@ using std::max;
     // Construction/Destruction
     //////////////////////////////////////////////////////////////////////
     
    -CUnitHandler* uh = NULL;
    +CUnitHandler* uh;
     
    -CR_BIND(CUnitHandler, );
    +CR_BIND(CUnitHandler, (true));
     CR_REG_METADATA(CUnitHandler, (
     	CR_MEMBER(activeUnits),
     	CR_MEMBER(units),
    -	CR_MEMBER(freeUnitIDs),
    -	CR_MEMBER(maxUnits),
    -	CR_MEMBER(maxUnitsPerTeam),
    +	CR_MEMBER(freeIDs),
    +	CR_MEMBER(waterDamage),
    +	CR_MEMBER(unitsPerTeam),
     	CR_MEMBER(maxUnitRadius),
    -	CR_MEMBER(unitsToBeRemoved),
    +	CR_MEMBER(toBeRemoved),
     	CR_MEMBER(morphUnitToFeature),
    +//	CR_MEMBER(toBeRemoved),
     	CR_MEMBER(builderCAIs),
     	CR_MEMBER(unitsByDefs),
     	CR_POSTLOAD(PostLoad),
     	CR_SERIALIZER(Serialize)
    -));
    +	));
    +
     
    +void CUnitHandler::Serialize(creg::ISerializer& s)
    +{
    +}
     
     
     void CUnitHandler::PostLoad()
    @@ -66,55 +74,38 @@ void CUnitHandler::PostLoad()
     }
     
     
    -CUnitHandler::CUnitHandler()
    +CUnitHandler::CUnitHandler(bool serializing)
     :
     	maxUnitRadius(0.0f),
     	morphUnitToFeature(true)
     {
    -	assert(teamHandler->ActiveTeams() > 0);
    -
    -	// note: the number of active teams can change at run-time, so
    -	// the team unit limit should be recalculated whenever one dies
    -	// or spawns (but would get complicated)
    -	maxUnits = std::min(gameSetup->maxUnits * teamHandler->ActiveTeams(), MAX_UNITS);
    -	maxUnitsPerTeam = maxUnits / teamHandler->ActiveTeams();
    -
    -	// ensure each team can make at least one unit
    -	assert(maxUnits >= teamHandler->ActiveTeams());
    -	assert(maxUnitsPerTeam >= 1);
    -
    -	units.resize(maxUnits, NULL);
    -	unitsByDefs.resize(teamHandler->ActiveTeams(), std::vector<CUnitSet>(unitDefHandler->unitDefs.size()));
    +	const size_t maxUnitsTemp = std::min(gameSetup->maxUnits * teamHandler->ActiveTeams(), MAX_UNITS);
    +	units.resize(maxUnitsTemp);
    +	unitsPerTeam = maxUnitsTemp / teamHandler->ActiveTeams() - 5;
    +
    +	freeIDs.reserve(units.size()-1);
    +	for (size_t a = 1; a < units.size(); a++) {
    +		freeIDs.push_back(a);
    +		units[a] = NULL;
    +	}
    +	units[0] = NULL;
     
    -	{
    -		std::vector<unsigned int> freeIDs(units.size());
    +	slowUpdateIterator = activeUnits.end();
     
    -		// id's are used as indices, so they must lie in [0, units.size() - 1]
    -		// (furthermore all id's are treated equally, none have special status)
    -		for (unsigned int id = 0; id < units.size(); id++) {
    -			freeIDs[id] = id;
    -		}
    +	waterDamage = mapInfo->water.damage;
     
    -		// randomize the unit ID's so that Lua widgets can not
    -		// easily determine enemy unit counts from ID's alone
    -		// (shuffle twice for good measure)
    -		SyncedRNG rng;
    +	if (!serializing) {
    +		airBaseHandler = new CAirBaseHandler;
     
    -		std::random_shuffle(freeIDs.begin(), freeIDs.end(), rng);
    -		std::random_shuffle(freeIDs.begin(), freeIDs.end(), rng);
    -		std::copy(freeIDs.begin(), freeIDs.end(), std::front_inserter(freeUnitIDs));
    +		unitsByDefs.resize(teamHandler->ActiveTeams(), std::vector<CUnitSet>(unitDefHandler->unitDefs.size()));
     	}
    -
    -	slowUpdateIterator = activeUnits.end();
    -	airBaseHandler = new CAirBaseHandler();
     }
     
     
     CUnitHandler::~CUnitHandler()
     {
     	for (std::list<CUnit*>::iterator usi = activeUnits.begin(); usi != activeUnits.end(); ++usi) {
    -		// ~CUnit dereferences featureHandler which is destroyed already
    -		(*usi)->delayedWreckLevel = -1;
    +		(*usi)->delayedWreckLevel = -1; // dont create wreckages since featureHandler may be destroyed already
     		delete (*usi);
     	}
     
    @@ -122,44 +113,40 @@ CUnitHandler::~CUnitHandler()
     }
     
     
    -bool CUnitHandler::AddUnit(CUnit *unit)
    +int CUnitHandler::AddUnit(CUnit *unit)
     {
    -	if (freeUnitIDs.empty()) {
    -		// should be unreachable (all code that goes through
    -		// UnitLoader::LoadUnit --> Unit::PreInit checks the
    -		// unit limit first)
    -		assert(false);
    -		return false;
    -	}
    -
    -	unit->id = freeUnitIDs.front();
    -	units[unit->id] = unit;
    -
    +	int num = (int)(gs->randFloat()) * ((int)activeUnits.size() - 1);
     	std::list<CUnit*>::iterator ui = activeUnits.begin();
    -
    -	if (ui != activeUnits.end()) {
    -		// randomize this to make the slow-update order random (good if one
    -		// builds say many buildings at once and then many mobile ones etc)
    -		const unsigned int insertionPos = gs->randFloat() * activeUnits.size();
    -
    -		for (unsigned int n = 0; n < insertionPos; ++n) {
    -			++ui;
    -		}
    +	for (int a = 0; a < num;++a) {
    +		++ui;
     	}
     
    +	// randomize this to make the order in slowupdate random (good if one
    +	// builds say many buildings at once and then many mobile ones etc)
     	activeUnits.insert(ui, unit);
    -	freeUnitIDs.pop_front();
     
    +	// randomize the unitID assignment so that lua widgets can
    +	// not easily determine enemy unit counts from unitIDs alone
    +	assert(!freeIDs.empty());
    +	const unsigned int freeSlot = gs->randInt() % freeIDs.size();
    +	const unsigned int freeMax  = freeIDs.size() - 1;
    +	unit->id = freeIDs[freeSlot]; // set the unit ID
    +	freeIDs[freeSlot] = freeIDs[freeMax];
    +	freeIDs.resize(freeMax);
    +
    +	units[unit->id] = unit;
     	teamHandler->Team(unit->team)->AddUnit(unit, CTeam::AddBuilt);
     	unitsByDefs[unit->team][unit->unitDef->id].insert(unit);
     
     	maxUnitRadius = max(unit->radius, maxUnitRadius);
    -	return true;
    +
    +	return unit->id;
     }
     
    +
     void CUnitHandler::DeleteUnit(CUnit* unit)
     {
    -	unitsToBeRemoved.push_back(unit);
    +	toBeRemoved.push_back(unit);
     	(eventBatchHandler->GetUnitCreatedDestroyedBatch()).dequeue_synced(unit);
     }
     
    @@ -187,12 +174,13 @@ void CUnitHandler::DeleteUnitNow(CUnit* delUnit)
     
     			activeUnits.erase(usi);
     			units[delUnit->id] = 0;
    -			freeUnitIDs.push_back(delUnit->id);
    +			freeIDs.push_back(delUnit->id);
     			teamHandler->Team(delTeam)->RemoveUnit(delUnit, CTeam::RemoveDied);
     
     			unitsByDefs[delTeam][delType].erase(delUnit);
     
     			delete delUnit;
    +
     			break;
     		}
     	}
    @@ -215,7 +203,7 @@ void CUnitHandler::Update()
     	{
     		GML_STDMUTEX_LOCK(runit); // Update
     
    -		if (!unitsToBeRemoved.empty()) {
    +		if (!toBeRemoved.empty()) {
     			GML_RECMUTEX_LOCK(unit); // Update - for anti-deadlock purposes.
     			GML_RECMUTEX_LOCK(sel);  // Update - unit is removed from selectedUnits in ~CObject, which is too late.
     			GML_RECMUTEX_LOCK(quad); // Update - make sure unit does not get partially deleted before before being removed from the quadfield
    @@ -223,9 +211,9 @@ void CUnitHandler::Update()
     
     			eventHandler.DeleteSyncedUnits();
     
    -			while (!unitsToBeRemoved.empty()) {
    -				CUnit* delUnit = unitsToBeRemoved.back();
    -				unitsToBeRemoved.pop_back();
    +			while (!toBeRemoved.empty()) {
    +				CUnit* delUnit = toBeRemoved.back();
    +				toBeRemoved.pop_back();
     
     				DeleteUnitNow(delUnit);
     			}
    @@ -237,7 +225,7 @@ void CUnitHandler::Update()
     	GML_UPDATE_TICKS();
     
     	{
    -		SCOPED_TIMER("Unit::MoveType::Update");
    +		SCOPED_TIMER("Unit Movetype update");
     		std::list<CUnit*>::iterator usi;
     		for (usi = activeUnits.begin(); usi != activeUnits.end(); ++usi) {
     			CUnit* unit = *usi;
    @@ -252,7 +240,7 @@ void CUnitHandler::Update()
     	}
     
     	{
    -		SCOPED_TIMER("Unit::Update");
    +		SCOPED_TIMER("Unit update");
     		std::list<CUnit*>::iterator usi;
     		for (usi = activeUnits.begin(); usi != activeUnits.end(); ++usi) {
     			CUnit* unit = *usi;
    @@ -272,7 +260,7 @@ void CUnitHandler::Update()
     	}
     
     	{
    -		SCOPED_TIMER("Unit::SlowUpdate");
    +		SCOPED_TIMER("Unit slow update");
     		if (!(gs->frameNum & (UNIT_SLOWUPDATE_RATE - 1))) {
     			slowUpdateIterator = activeUnits.begin();
     		}
    @@ -285,10 +273,10 @@ void CUnitHandler::Update()
     }
     
     
    -float CUnitHandler::GetBuildHeight(const float3& pos, const UnitDef* unitdef)
    +float CUnitHandler::GetBuildHeight(float3 pos, const UnitDef* unitdef)
     {
    -	float minh = -5000.0f;
    -	float maxh =  5000.0f;
    +	float minh=-5000;
    +	float maxh=5000;
     	int numBorder=0;
     	float borderh=0;
     	const float* heightmap=readmap->GetHeightmap();
    @@ -477,9 +465,10 @@ void CUnitHandler::AddBuilderCAI(CBuilderCAI* b)
     {
     	GML_STDMUTEX_LOCK(cai); // AddBuilderCAI
     
    -	builderCAIs.insert(builderCAIs.end(), b);
    +	builderCAIs.insert(builderCAIs.end(),b);
     }
     
    +
     void CUnitHandler::RemoveBuilderCAI(CBuilderCAI* b)
     {
     	GML_STDMUTEX_LOCK(cai); // RemoveBuilderCAI
    @@ -488,51 +477,46 @@ void CUnitHandler::RemoveBuilderCAI(CBuilderCAI* b)
     }
     
     
    +void CUnitHandler::LoadSaveUnits(CLoadSaveInterface* file, bool loading)
    +{
    +}
    +
     
     /**
      * Returns a build Command that intersects the ray described by pos and dir from
      * the command queues of the units 'units' on team number 'team'.
      * @brief returns a build Command that intersects the ray described by pos and dir
    - * @return the build Command, or a Command with id 0 if none is found
    + * @return the build Command, or a Command wiht id 0 if none is found
      */
    -Command CUnitHandler::GetBuildCommand(const float3& pos, const float3& dir) {
    +Command CUnitHandler::GetBuildCommand(float3 pos, float3 dir){
     	float3 tempF1 = pos;
     
     	GML_STDMUTEX_LOCK(cai); // GetBuildCommand
     
     	CCommandQueue::iterator ci;
    -	for (std::list<CUnit*>::const_iterator ui = activeUnits.begin(); ui != activeUnits.end(); ++ui) {
    -		const CUnit* unit = *ui;
    -
    -		if (unit->team != gu->myTeam) {
    -			continue;
    -		}
    -
    -		ci = unit->commandAI->commandQue.begin();
    -
    -		for (; ci != unit->commandAI->commandQue.end(); ++ci) {
    -			const Command& cmd = *ci;
    -
    -			if (cmd.id < 0 && cmd.params.size() >= 3) {
    -				BuildInfo bi(cmd);
    -				tempF1 = pos + dir * ((bi.pos.y - pos.y) / dir.y) - bi.pos;
    -
    -				if (bi.def && (bi.GetXSize() / 2) * SQUARE_SIZE > fabs(tempF1.x) && (bi.GetZSize() / 2) * SQUARE_SIZE > fabs(tempF1.z)) {
    -					return cmd;
    +	for(std::list<CUnit*>::iterator ui = this->activeUnits.begin(); ui != this->activeUnits.end(); ++ui){
    +		if((*ui)->team == gu->myTeam){
    +			ci = (*ui)->commandAI->commandQue.begin();
    +			for(; ci != (*ui)->commandAI->commandQue.end(); ++ci){
    +				if((*ci).id < 0 && (*ci).params.size() >= 3){
    +					BuildInfo bi(*ci);
    +					tempF1 = pos + dir*((bi.pos.y - pos.y)/dir.y) - bi.pos;
    +					if(bi.def && bi.GetXSize()/2*SQUARE_SIZE > fabs(tempF1.x) && bi.GetZSize()/2*SQUARE_SIZE > fabs(tempF1.z)){
    +						return (*ci);
    +					}
     				}
     			}
     		}
     	}
    -
     	Command c;
    -	c.id = CMD_STOP;
    +	c.id = 0;
     	return c;
     }
     
     
     bool CUnitHandler::CanBuildUnit(const UnitDef* unitdef, int team)
     {
    -	if (teamHandler->Team(team)->units.size() >= maxUnitsPerTeam) {
    +	if (teamHandler->Team(team)->units.size() >= unitsPerTeam) {
     		return false;
     	}
     	if (unitsByDefs[team][unitdef->id].size() >= unitdef->maxThisUnit) {
    diff --git a/rts/Sim/Units/UnitHandler.h b/rts/Sim/Units/UnitHandler.h
    index 0f348d7..407f4c7 100644
    --- a/rts/Sim/Units/UnitHandler.h
    +++ b/rts/Sim/Units/UnitHandler.h
    @@ -24,15 +24,14 @@ class CUnitHandler
     	CR_DECLARE(CUnitHandler)
     
     public:
    -	CUnitHandler();
    -	~CUnitHandler();
    -
     	void Update();
     	void DeleteUnit(CUnit* unit);
     	void DeleteUnitNow(CUnit* unit);
    -	bool AddUnit(CUnit* unit);
    -	void Serialize(creg::ISerializer& s) {}
    +	int AddUnit(CUnit* unit);
    +	CUnitHandler(bool serializing=false);
    +	void Serialize(creg::ISerializer& s);
     	void PostLoad();
    +	virtual ~CUnitHandler();
     
     	///< test if a unit can be built at specified position
     	///<   return values for the following is
    @@ -56,12 +55,20 @@ public:
     
     	void AddBuilderCAI(CBuilderCAI*);
     	void RemoveBuilderCAI(CBuilderCAI*);
    -	float GetBuildHeight(const float3& pos, const UnitDef* unitdef);
    +	float GetBuildHeight(float3 pos, const UnitDef* unitdef);
    +
    +	void LoadSaveUnits(CLoadSaveInterface* file, bool loading);
    +	Command GetBuildCommand(float3 pos, float3 dir);
     
    -	Command GetBuildCommand(const float3& pos, const float3& dir);
    +	int MaxUnitsPerTeam() const
    +	{
    +		return unitsPerTeam;
    +	};
     
    -	unsigned int MaxUnitsPerTeam() const { return maxUnitsPerTeam; }
    -	unsigned int MaxUnits() const { return maxUnits; }
    +	size_t MaxUnits() const
    +	{
    +		return units.size();
    +	};
     
     
     	// note: negative ID's are implicitly converted
    @@ -72,19 +79,23 @@ public:
     	std::vector< std::vector<CUnitSet> > unitsByDefs; ///< units sorted by team and unitDef
     
     	std::list<CUnit*> activeUnits;                    ///< used to get all active units
    +	std::vector<int> freeIDs;
     	std::vector<CUnit*> units;                        ///< used to get units from IDs (0 if not created)
    +
    +	std::vector<CUnit*> toBeRemoved;                  ///< units that will be removed at start of next update
    +
    +	std::list<CUnit*>::iterator slowUpdateIterator;
    +
     	std::list<CBuilderCAI*> builderCAIs;
     
    +	float waterDamage;
    +
     	float maxUnitRadius; ///< largest radius seen so far
    +
     	bool morphUnitToFeature;
     
     private:
    -	std::list<unsigned int> freeUnitIDs;
    -	std::vector<CUnit*> unitsToBeRemoved;            ///< units that will be removed at start of next update
    -	std::list<CUnit*>::iterator slowUpdateIterator;
    -
    -	unsigned int maxUnits;
    -	unsigned int maxUnitsPerTeam;
    +	int unitsPerTeam;
     };
     
     extern CUnitHandler* uh;
    diff --git a/rts/Sim/Units/UnitLoader.cpp b/rts/Sim/Units/UnitLoader.cpp
    index 47210de..fa95845 100644
    --- a/rts/Sim/Units/UnitLoader.cpp
    +++ b/rts/Sim/Units/UnitLoader.cpp
    @@ -315,8 +315,8 @@ void CUnitLoader::GiveUnits(const std::vector<std::string>& args, int team)
     	}
     
     	if (objectName == "all") {
    -		unsigned int numRequestedUnits = unitDefHandler->unitDefs.size() - 1; /// defid=0 is not valid
    -		unsigned int currentNumUnits = teamHandler->Team(team)->units.size();
    +		int numRequestedUnits = unitDefHandler->unitDefs.size() - 1; /// defid=0 is not valid
    +		int currentNumUnits = teamHandler->Team(team)->units.size();
     
     		// make sure team unit-limit is not exceeded
     		if ((currentNumUnits + numRequestedUnits) > uh->MaxUnitsPerTeam()) {
    @@ -345,14 +345,11 @@ void CUnitLoader::GiveUnits(const std::vector<std::string>& args, int team)
     			}
     		}
     	} else {
    -		unsigned int numRequestedUnits = amount;
    -		unsigned int currentNumUnits = teamHandler->Team(team)->units.size();
    +		int numRequestedUnits = amount;
    +		int currentNumUnits = teamHandler->Team(team)->units.size();
     
     		if (currentNumUnits >= uh->MaxUnitsPerTeam()) {
    -			logOutput.Print(
    -				"[%s] unable to give more units to team %d (current: %u, team limit: %u, global limit: %u)",
    -				__FUNCTION__, team, currentNumUnits, uh->MaxUnitsPerTeam(), uh->MaxUnits()
    -			);
    +			logOutput.Print("[%s] unable to give more units to team %d (current: %d, max: %d)", __FUNCTION__, team, currentNumUnits, uh->MaxUnits());
     			return;
     		}
     
    diff --git a/rts/Sim/Units/UnitTypes/TransportUnit.cpp b/rts/Sim/Units/UnitTypes/TransportUnit.cpp
    index 72faaa3..470d6f9 100644
    --- a/rts/Sim/Units/UnitTypes/TransportUnit.cpp
    +++ b/rts/Sim/Units/UnitTypes/TransportUnit.cpp
    @@ -77,19 +77,16 @@ void CTransportUnit::Update()
     		if (unitDef->holdSteady) {
     			// slave transportee orientation to piece
     			if (ti->piece >= 0) {
    -				const CMatrix44f& transMat = GetTransformMatrix(true);
     				const CMatrix44f& pieceMat = script->GetPieceMatrix(ti->piece);
    -				const CMatrix44f  slaveMat = transMat.Mul(pieceMat);
    -
    -				SyncedFloat3& xdir = transportee->rightdir;
    -				SyncedFloat3& ydir = transportee->updir;
    -				SyncedFloat3& zdir = transportee->frontdir;
    -
    -				zdir.x = slaveMat[8]; zdir.y = slaveMat[9]; zdir.z = slaveMat[10];
    -				xdir.x = slaveMat[0]; xdir.y = slaveMat[1]; xdir.z = slaveMat[ 2];
    -				ydir.x = slaveMat[4]; ydir.y = slaveMat[5]; ydir.z = slaveMat[ 6];
    -
    -				transportee->heading = GetHeadingFromVector(zdir.x, zdir.z);
    +				const float3 pieceDir =
    +					frontdir * pieceMat[10] +
    +					updir    * pieceMat[ 6] +
    +					rightdir * pieceMat[ 2];
    +
    +				transportee->heading  = GetHeadingFromVector(pieceDir.x, pieceDir.z);
    +				transportee->frontdir = pieceDir;
    +				transportee->rightdir = transportee->frontdir.cross(UpVector);
    +				transportee->updir    = transportee->rightdir.cross(transportee->frontdir);
     			}
     		} else {
     			// slave transportee orientation to body
    diff --git a/rts/Sim/Weapons/Weapon.cpp b/rts/Sim/Weapons/Weapon.cpp
    index bc60b64..aa7bf15 100644
    --- a/rts/Sim/Weapons/Weapon.cpp
    +++ b/rts/Sim/Weapons/Weapon.cpp
    @@ -474,7 +474,7 @@ bool CWeapon::AttackGround(float3 pos, bool userTarget)
     		return false;
     	if (targetUnit) {
     		DeleteDeathDependence(targetUnit);
    -		targetUnit = NULL;
    +		targetUnit=0;
     	}
     
     	haveUserTarget = userTarget;
    @@ -531,7 +531,7 @@ bool CWeapon::AttackUnit(CUnit* unit, bool userTarget)
     
     	if (targetUnit) {
     		DeleteDeathDependence(targetUnit);
    -		targetUnit = NULL;
    +		targetUnit = 0;
     	}
     
     	haveUserTarget = userTarget;
    @@ -549,7 +549,7 @@ void CWeapon::HoldFire()
     {
     	if (targetUnit) {
     		DeleteDeathDependence(targetUnit);
    -		targetUnit = NULL;
    +		targetUnit = 0;
     	}
     	targetType = Target_None;
     	haveUserTarget = false;
    @@ -668,7 +668,7 @@ void CWeapon::SlowUpdate(bool noAutoTargetOverride)
     		// use targets from the thing we are slaved to
     		if (targetUnit) {
     			DeleteDeathDependence(targetUnit);
    -			targetUnit = NULL;
    +			targetUnit = 0;
     		}
     		targetType = Target_None;
     
    @@ -698,34 +698,22 @@ void CWeapon::SlowUpdate(bool noAutoTargetOverride)
     		lastTargetRetry = gs->frameNum;
     
     		std::multimap<float, CUnit*> targets;
    -		std::multimap<float, CUnit*>::const_iterator nextTargetIt;
    -		std::multimap<float, CUnit*>::const_iterator lastTargetIt;
    -
     		helper->GenerateWeaponTargets(this, targetUnit, targets);
     
    -		if (!targets.empty())
    -			lastTargetIt = --targets.end();
    +		for (std::multimap<float, CUnit*>::const_iterator ti = targets.begin(); ti != targets.end(); ++ti) {
    +			CUnit* nextTarget = ti->second;
     
    -		for (nextTargetIt = targets.begin(); nextTargetIt != targets.end(); ++nextTargetIt) {
    -			CUnit* nextTargetUnit = nextTargetIt->second;
    -
    -			if (nextTargetUnit->neutral && (owner->fireState <= FIRESTATE_FIREATWILL)) {
    +			if (nextTarget->neutral && (owner->fireState <= FIRESTATE_FIREATWILL)) {
     				continue;
     			}
    -
    -			// when only one target is available, <nextTarget> can equal <targetUnit>
    -			// and we want to attack whether it is in our bad target category or not
    -			// (if only bad targets are available and this is the last, just pick it)
    -			if (nextTargetUnit != targetUnit && (nextTargetUnit->category & badTargetCategory)) {
    -				if (nextTargetIt != lastTargetIt) {
    -					continue;
    -				}
    +			if (targetUnit && (nextTarget->category & badTargetCategory)) {
    +				continue;
     			}
     
    -			const float weaponLead = weaponDef->targetMoveError * GAME_SPEED * nextTargetUnit->speed.Length();
    +			const float weaponLead = weaponDef->targetMoveError * GAME_SPEED * nextTarget->speed.Length();
     			const float weaponError = weaponLead * (1.0f - owner->limExperience);
     
    -			float3 nextTargetPos = nextTargetUnit->midPos + (errorVector * weaponError);
    +			float3 nextTargetPos = nextTarget->midPos + (errorVector * weaponError);
     
     			const float appHeight = ground->GetApproximateHeight(nextTargetPos.x, nextTargetPos.z) + 2.0f;
     
    @@ -733,13 +721,13 @@ void CWeapon::SlowUpdate(bool noAutoTargetOverride)
     				nextTargetPos.y = appHeight;
     			}
     
    -			if (TryTarget(nextTargetPos, false, nextTargetUnit)) {
    +			if (TryTarget(nextTargetPos, false, nextTarget)) {
     				if (targetUnit) {
     					DeleteDeathDependence(targetUnit);
     				}
     
     				targetType = Target_Unit;
    -				targetUnit = nextTargetUnit;
    +				targetUnit = nextTarget;
     				targetPos = nextTargetPos;
     
     				AddDeathDependence(targetUnit);
    @@ -755,7 +743,7 @@ void CWeapon::SlowUpdate(bool noAutoTargetOverride)
     		}
     	} else {
     		// if we can't target anything, try switching aim point
    -		if (useWeaponPosForAim == 1) {
    +		if (useWeaponPosForAim && (useWeaponPosForAim == 1)) {
     			useWeaponPosForAim = 0;
     		} else {
     			useWeaponPosForAim = 1;
    
    diff file icon git.diff (133,696 bytes) 2011-04-01 02:20 +

-Relationships
related to 0002430resolvedKloot (master) crash in zero-k 0.8.7 when using gestures-menu 
+Relationships

-Notes

~0006521

abma (administrator)

Last edited: 2011-04-01 01:10

an other one:

http://pastebin.com/1149jyvz

~0006522

abma (administrator)

Last edited: 2011-04-01 01:11

damn, memory corruption?

http://pastebin.com/Li1AhhSC

~0006523

abma (administrator)

Last edited: 2011-04-01 02:21

a diff-file which lists changes between master and assimp branch before merge, created with this command:

$ git diff 5ac4738e8b0a886925e5e49a72cdf9803932f68a dcc943946a693b138dc85a8a391f6b1259c15798 -- rts/Game rts/Rendering rts/Sim rts/system

maybe it helps others

~0006524

abma (administrator)

an other one: (pulseaudio/efx related?)

http://pastebin.com/qnKEQstg

~0006547

abma (administrator)

Last edited: 2011-04-09 17:12

git bisect log
git bisect start
# bad: [078f69eef2811695044aaac271a4b1e3b7d7f461] fix gl.PushAttrib(GL.ALL_ATTRIB_BITS)
git bisect bad 078f69eef2811695044aaac271a4b1e3b7d7f461
# good: [66de8f60e863befa61e5addaea539877dd78d064] modularized Watchdog & added Linux implementation * it can now handle any number of threads * it gives threads names - currently "main" (:= render-thread in spring-mt) & "sim" (only in spring-mt) * syntax of the Lua function changed too: Spring.ClearWatchDogTimer() --clears current thread's timer Spring.ClearWatchDogTimer("sim") --clears the timer of the "sim" thread
git bisect good d4bc8acf371156afd2255286ae6328a00e7dd203
# good: [d4bc8acf371156afd2255286ae6328a00e7dd203] buildbot: ...and fix the path here too
git bisect good d4bc8acf371156afd2255286ae6328a00e7dd203
# bad: [3e60285d073d39a39fa01b7dd3770f166e47f6c5] Fix compile error
git bisect bad 3e60285d073d39a39fa01b7dd3770f166e47f6c5
# bad: [49413d80c5b92796b60dc51eff5381c1a7a1c841] Update VCproj
git bisect bad 49413d80c5b92796b60dc51eff5381c1a7a1c841
# good: [31800c902f961270e9440efadef8b7b3a9d85ae1] add php frontend for stacktrace_translator
git bisect good 31800c902f961270e9440efadef8b7b3a9d85ae1
# bad: [cc7a70b1134f024544d58b1b9a955f61f70f650c] compile
git bisect bad cc7a70b1134f024544d58b1b9a955f61f70f650c
# good: [04584519dd2257780def04a76bc4e486fa89ae9f] Audio: readd stats of dropped sounds
git bisect good 04584519dd2257780def04a76bc4e486fa89ae9f


so, the error seems to be there:
https://github.com/spring/spring/compare/04584519dd2257780def04a76bc4e486fa89ae9f...cc7a70b1134f024544d58b1b9a955f61f70f650c

~0006551

abma (administrator)

tried a few times again, 04584519dd2257780def04a76bc4e486fa89ae9f seems to be the last version without this bug.

~0006552

abma (administrator)

Last edited: 2011-04-11 19:29

i tried on the same computer but with windows, crashes too:

http://pastebin.com/HtTvHsGN

translated:

http://pastebin.com/xdP1LHR3

in linux i didn't get "LuaUI::RunCallIn: error = 4, GameFrame, not enough memory"

seems to happen only sometimes
i've 4GB mem...

~0006553

abma (administrator)

Last edited: 2011-04-11 21:48

an other one: (this time with zk chickens)
http://pastebin.com/KYq2emDe

translated:
http://pastebin.com/vUGQTDGJ

~0006555

abma (administrator)

ok, it looks like its only an out of memory. To reproduce it faster, this works for me as well:

/cheat
/give 300 armflea

CTRL+A -> crash

Tested in BA, "Red Build/Order Menu" has to be enabled

~0006558

jK (developer)

Last edited: 2011-04-16 12:34

k, a short expertise:

Lua uses a GarbageCollector, so it allocates more memory than needed.
Additionally, Lua has a few quirks. So it does _not_ save a counter for references, to detect unused objects it needs to check the whole environment.
Second, it doesn't have a "emergency GC", so Lua won't call the GC when running out of memory even when the environment is full unused/dead objects (will change with next Lua release)!
And now the only thing we can control: the way Lua creates tables. There are two ways to create tables in C: lua_newtable & lua_createtable.
Both behave similar to std::vectors. The first one just creates a vector/table and the second creates it + calls a .reserve() -> it allocs enough mem to hold all objects w/o the need to resize it.
But there is one important difference to c++ std::vectors, Lua uses a GC! So when a table is to small to hold all objects and needs to be resized IT WILL LIKELY CREATE A COMPLETE NEW ONE and LEAVES THE OLD ONE IN THE RAM UNTIL THE GC CATCHES IT! This can create _thousand_ of unused tables in the ram when using lua_newtable & filling the table with ~ 1000 entries (esp. when the GC gets called each ~3sec). - Note: it seems this is only the case for the array part of tables, for the hash one it doesn't need continuous region, so it will just alloc single blocks for those. That's why it gets influenced by rawseti/ settable.

~0006612

jK (developer)

works now?

~0006619

abma (administrator)

no... now it crashes after selecting commander.
gdb output:
http://pastebin.com/GieCGV3M

console output:
[f=0003296] Segmentation fault (SIGSEGV) in spring 0.82+.4.0 (Debug)
[f=0003296] Stacktrace:
[f=0003296] <0> /lib/x86_64-linux-gnu/libc.so.6(+0x33d80) [0x7ffff4831d80]
[f=0003296] <1> /var/tmp/local/spring/master/bin/spring() [0xa37b4e]
[f=0003296] <2> /var/tmp/local/spring/master/bin/spring() [0xa74341]
[f=0003296] <3> /var/tmp/local/spring/master/bin/spring() [0xa75785]
[f=0003296] <4> /usr/include/c++/4.6/bits/vector.tcc:310
[f=0003296] <5> /usr/include/c++/4.6/bits/vector.tcc:363
[f=0003296] <6> /home/abma/dev/spring/master/rts/Lua/LuaUnitRendering.cpp:442
[f=0003296] <7> /home/abma/dev/spring/master/rts/Lua/LuaUnitRendering.cpp:472
[f=0003296] <8> /home/abma/dev/spring/master/rts/Lua/LuaUnitRendering.cpp:478
[f=0003296] <9> /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xff) [0x7ffff481ceff]
[New Thread 0x7fffd9323700 (LWP 4382)]
[f=0003296] WARNING: failed to shutdown normally, exit forced
[f=0003296] Spring crashed Spring has crashed:
Segmentation fault (SIGSEGV).

A stacktrace has been written to:
  /home/abma/.spring/infolog.txt
[Thread 0x7fffda425700 (LWP 4363) exited]
[Thread 0x7fffdac26700 (LWP 4362) exited]
AL lib: ALc.c:1818: alcCloseDevice(): deleting 247 Buffer(s)
[Thread 0x7fffdbc62700 (LWP 4361) exited]

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff50e5663 in __dynamic_cast ()
   from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
(gdb) quit
A debugging session is active.

    Inferior 1 [process 4357] will be killed.


with current master and "Red Build/Order Menu" is enabled, no menu is displayed, it also sometimes crashes already when selecting the com.

infolog.txt of a non-crash start: http://pastebin.com/ZJshK3Vi


when "Red Build/Order Menu" is disabled it seems to always work.

~0006626

jK (developer)

build with DEBUG2 and you should get a clean stacktrace

~0006638

abma (administrator)

http://pastebin.com/QYv5DwiG

~0006648

SirMaverick (reporter)

Did you try ZK? It freezes for me in master, when I open the build menu (press b on constructor). Related or separate bug?

~0006681

Kloot (developer)

Does this still happen after ea431c3d23?

~0006682

abma (administrator)

Last edited: 2011-05-31 23:44

no, doesn't crash, this seems to be fixed, thx! :-)

but the zero-k buildmenu / "Red Build/Order Menu" isn't displayed, don't know whats wrong there....

when "Red Build/Order Menu" widget is loaded, the background box can be seen (without buttons) but it fades out and isn't shown again when a builder is selected.

gestures in zero-k work, and the default menu is shown!

~0006683

abma (administrator)

i'll open a new report for that issue, the initial problem is solved, thx! :)
+Notes

-Issue History
Date Modified Username Field Change
2011-04-01 00:20 abma New Issue
2011-04-01 00:27 abma Note Added: 0006521
2011-04-01 01:09 abma Note Added: 0006522
2011-04-01 01:10 abma Additional Information Updated
2011-04-01 01:10 abma Note Edited: 0006521
2011-04-01 01:11 abma Note Edited: 0006522
2011-04-01 02:20 abma File Added: git.diff
2011-04-01 02:21 abma Note Added: 0006523
2011-04-01 02:21 abma Note Edited: 0006523
2011-04-01 03:42 abma Summary crash after assimp merge => crash because of assimp merge (?)
2011-04-01 03:43 abma Additional Information Updated
2011-04-01 11:53 abma Note Added: 0006524
2011-04-01 11:56 abma Description Updated
2011-04-09 16:56 abma Note Added: 0006547
2011-04-09 16:57 abma Note Edited: 0006547
2011-04-09 17:12 abma Note Edited: 0006547
2011-04-11 15:30 abma Note Added: 0006551
2011-04-11 15:30 abma Summary crash because of assimp merge (?) => crash/mem corruption(?) after commit 04584519dd2257780def04a76bc4e486fa89ae9f
2011-04-11 18:55 abma Note Added: 0006552
2011-04-11 18:57 abma Note Edited: 0006552
2011-04-11 19:29 abma Note Edited: 0006552
2011-04-11 21:47 abma Note Added: 0006553
2011-04-11 21:48 abma Note Edited: 0006553
2011-04-13 20:38 abma Note Added: 0006555
2011-04-13 20:39 abma Summary crash/mem corruption(?) after commit 04584519dd2257780def04a76bc4e486fa89ae9f => Crash in spring master with "Red Build/Order Menu" and >300 Units when pressing CTRL+A
2011-04-13 20:39 abma Summary Crash in spring master with "Red Build/Order Menu" and >300 Units when pressing CTRL+A => Crash in spring master with "Red Build/Order Menu" and >300 Units when pressing CTRL+A (Out of Memory)
2011-04-13 20:39 abma Reproducibility sometimes => always
2011-04-16 12:34 jK Note Added: 0006558
2011-04-16 12:34 jK Note Edited: 0006558
2011-05-07 14:46 jK Note Added: 0006612
2011-05-07 21:55 abma Note Added: 0006619
2011-05-09 13:25 jK Note Added: 0006626
2011-05-11 01:33 abma Note Added: 0006638
2011-05-11 17:57 SirMaverick Note Added: 0006648
2011-05-15 21:13 abma Relationship added related to 0002430
2011-05-31 15:16 abma Summary Crash in spring master with "Red Build/Order Menu" and >300 Units when pressing CTRL+A (Out of Memory) => (master) crash with "Red Build/Order Menu" and >300 Units when pressing CTRL+A (Out of Memory)
2011-05-31 23:19 Kloot Note Added: 0006681
2011-05-31 23:19 Kloot Status new => feedback
2011-05-31 23:30 abma Note Added: 0006682
2011-05-31 23:31 abma Note Edited: 0006682
2011-05-31 23:42 abma Note Edited: 0006682
2011-05-31 23:44 abma Note Edited: 0006682
2011-05-31 23:44 abma Note Edited: 0006682
2011-05-31 23:47 abma Note Added: 0006683
2011-05-31 23:47 abma Status feedback => resolved
2011-05-31 23:47 abma Resolution open => fixed
2011-05-31 23:47 abma Assigned To => abma
+Issue History