From 2012b7e22cc9bd1c52a74478450f07e73511f8cc Mon Sep 17 00:00:00 2001 From: Mark Vejvoda Date: Sat, 13 Mar 2010 21:10:45 +0000 Subject: [PATCH] Made changes to properly isolate new unit rotation code so that it is disabled by default --- mk/linux/configuration.xml | 30 + mk/linux/glest.ini | 5 +- source/configurator/configuration.cpp | 2 +- source/glest_game/game/game.cpp | 897 +++++ source/glest_game/global/config.cpp | 89 + source/glest_game/global/config.h | 58 + source/glest_game/graphics/renderer.cpp | 2892 +++++++++++++++++ source/glest_game/graphics/renderer.h | 309 ++ source/glest_game/gui/gui.cpp | 19 +- source/glest_game/gui/gui.h | 207 ++ source/glest_game/main/program.cpp | 270 ++ source/glest_game/network/network_interface.h | 211 ++ source/glest_game/type_instances/unit.cpp | 94 +- source/glest_game/type_instances/unit.h | 316 ++ source/glest_game/world/unit_updater.cpp | 898 +++++ source/glest_game/world/unit_updater.h | 115 + source/glest_game/world/world.cpp | 20 +- source/glest_game/world/world.h | 167 + source/glest_map_editor/main.cpp | 886 +++++ source/shared_lib/include/graphics/model.h | 172 + source/shared_lib/include/util/properties.h | 80 + .../shared_lib/sources/graphics/gl/opengl.cpp | 151 + .../sources/graphics/gl/texture_gl.cpp | 373 +++ source/shared_lib/sources/graphics/model.cpp | 525 +++ .../sources/graphics/texture_manager.cpp | 96 + source/shared_lib/sources/util/conversion.cpp | 126 + source/shared_lib/sources/util/properties.cpp | 224 ++ 27 files changed, 9174 insertions(+), 58 deletions(-) create mode 100644 source/glest_game/game/game.cpp create mode 100644 source/glest_game/global/config.cpp create mode 100644 source/glest_game/global/config.h create mode 100644 source/glest_game/graphics/renderer.cpp create mode 100644 source/glest_game/graphics/renderer.h create mode 100644 source/glest_game/gui/gui.h create mode 100644 source/glest_game/main/program.cpp create mode 100644 source/glest_game/network/network_interface.h create mode 100644 source/glest_game/type_instances/unit.h create mode 100644 source/glest_game/world/unit_updater.cpp create mode 100644 source/glest_game/world/unit_updater.h create mode 100644 source/glest_game/world/world.h create mode 100755 source/glest_map_editor/main.cpp create mode 100644 source/shared_lib/include/graphics/model.h create mode 100644 source/shared_lib/include/util/properties.h create mode 100644 source/shared_lib/sources/graphics/gl/opengl.cpp create mode 100644 source/shared_lib/sources/graphics/gl/texture_gl.cpp create mode 100644 source/shared_lib/sources/graphics/model.cpp create mode 100644 source/shared_lib/sources/graphics/texture_manager.cpp create mode 100644 source/shared_lib/sources/util/conversion.cpp create mode 100644 source/shared_lib/sources/util/properties.cpp diff --git a/mk/linux/configuration.xml b/mk/linux/configuration.xml index bbf5d65b..95d3a978 100644 --- a/mk/linux/configuration.xml +++ b/mk/linux/configuration.xml @@ -342,6 +342,26 @@ when they are issued a command"/> + + + + + + + + + + + + + + + + + + + + @@ -373,6 +393,16 @@ when they are issued a command"/> + + + + + + + + + + diff --git a/mk/linux/glest.ini b/mk/linux/glest.ini index a7ebd239..462a3f68 100644 --- a/mk/linux/glest.ini +++ b/mk/linux/glest.ini @@ -2,6 +2,9 @@ AiLog=0 AiRedir=0 +AllowDownloadDataSynch=0 +AllowGameDataSynchCheck=0 +AllowRotateUnits=0 AutoTest=0 CheckGlCaps=1 ColorBits=32 @@ -30,7 +33,7 @@ PhotoMode=0 RefreshFrequency=75 ScreenHeight=984 ScreenWidth=1680 -ServerIp=192.168.1.1 +ServerIp=192.168.0.107 ServerPort=6666 ShadowAlpha=0.2 ShadowFrameSkip=2 diff --git a/source/configurator/configuration.cpp b/source/configurator/configuration.cpp index 8c583bd0..8cbe5285 100644 --- a/source/configurator/configuration.cpp +++ b/source/configurator/configuration.cpp @@ -70,7 +70,7 @@ void Configuration::loadValues(const string &path){ FieldGroup *fg= fieldGroups[i]; for(int j=0; jgetFieldCount(); ++j){ Field *f= fg->getField(j); - f->setValue(properties.getString(f->getVariableName())); + f->setValue(properties.getString(f->getVariableName(),"")); } } } diff --git a/source/glest_game/game/game.cpp b/source/glest_game/game/game.cpp new file mode 100644 index 00000000..8145a6da --- /dev/null +++ b/source/glest_game/game/game.cpp @@ -0,0 +1,897 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "game.h" + +#include "config.h" +#include "renderer.h" +#include "particle_renderer.h" +#include "commander.h" +#include "battle_end.h" +#include "sound_renderer.h" +#include "profiler.h" +#include "core_data.h" +#include "metrics.h" +#include "faction.h" +#include "network_manager.h" +#include "checksum.h" +#include "auto_test.h" +#include "leak_dumper.h" + +using namespace Shared::Graphics; +using namespace Shared::Util; +using namespace Shared::Platform; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Game +// ===================================================== + +// ===================== PUBLIC ======================== + +Game::Game(Program *program, const GameSettings *gameSettings): + ProgramState(program) +{ + this->gameSettings= *gameSettings; + + mouseX=0; + mouseY=0; + mouse2d= 0; + loadingText=""; + weatherParticleSystem= NULL; + updateFps=0; + renderFps=0; + lastUpdateFps=0; + lastRenderFps=0; + paused= false; + gameOver= false; + renderNetworkStatus= false; + speed= sNormal; +} + +Game::~Game(){ + Logger &logger= Logger::getInstance(); + Renderer &renderer= Renderer::getInstance(); + + logger.setState(Lang::getInstance().get("Deleting")); + logger.add("Game", true); + + renderer.endGame(); + SoundRenderer::getInstance().stopAllSounds(); + + deleteValues(aiInterfaces.begin(), aiInterfaces.end()); + + gui.end(); //selection must be cleared before deleting units + world.end(); //must die before selection because of referencers +} + + +// ==================== init and load ==================== + +void Game::load(){ + Logger &logger= Logger::getInstance(); + string mapName= gameSettings.getMap(); + string tilesetName= gameSettings.getTileset(); + string techName= gameSettings.getTech(); + string scenarioName= gameSettings.getScenario(); + + logger.setState(Lang::getInstance().get("Loading")); + + if(scenarioName.empty()){ + logger.setSubtitle(formatString(mapName)+" - "+formatString(tilesetName)+" - "+formatString(techName)); + } + else{ + logger.setSubtitle(formatString(scenarioName)); + } + + //tileset + world.loadTileset("tilesets/"+tilesetName, &checksum); + + set factions; + for ( int i=0; i < gameSettings.getFactionCount(); ++i ) { + factions.insert(gameSettings.getFactionTypeName(i)); + } + + //tech, load before map because of resources + world.loadTech("techs/"+techName, factions, &checksum); + + //map + world.loadMap(Map::getMapPath(mapName), &checksum); + + //scenario + if(!scenarioName.empty()){ + Lang::getInstance().loadScenarioStrings(gameSettings.getScenarioDir(), scenarioName); + world.loadScenario(Scenario::getScenarioPath(gameSettings.getScenarioDir(), scenarioName), &checksum); + } +} + +void Game::init() +{ + if(Socket::enableDebugText) printf("In [%s::%s] START\n",__FILE__,__FUNCTION__); + + Lang &lang= Lang::getInstance(); + Logger &logger= Logger::getInstance(); + CoreData &coreData= CoreData::getInstance(); + Renderer &renderer= Renderer::getInstance(); + Map *map= world.getMap(); + NetworkManager &networkManager= NetworkManager::getInstance(); + + if(Socket::enableDebugText) printf("In [%s::%s] Initializing\n",__FILE__,__FUNCTION__); + logger.setState(lang.get("Initializing")); + + //mesage box + mainMessageBox.init(lang.get("Yes"), lang.get("No")); + mainMessageBox.setEnabled(false); + + //check fog of war + if(!Config::getInstance().getBool("FogOfWar") && networkManager.isNetworkGame() ){ + throw runtime_error("Can not play online games with for of war disabled"); + } + + //init world, and place camera + commander.init(&world); + world.init(this, gameSettings.getDefaultUnits()); + gui.init(this); + chatManager.init(&console, world.getThisTeamIndex()); + const Vec2i &v= map->getStartLocation(world.getThisFaction()->getStartLocationIndex()); + gameCamera.init(map->getW(), map->getH()); + gameCamera.setPos(Vec2f(v.x, v.y)); + scriptManager.init(&world, &gameCamera); + + if(Socket::enableDebugText) printf("In [%s::%s] creating AI's\n",__FILE__,__FUNCTION__); + + //create IAs + aiInterfaces.resize(world.getFactionCount()); + for(int i=0; igetCpuControl()){ + aiInterfaces[i]= new AiInterface(*this, i, faction->getTeam()); + logger.add("Creating AI for faction " + intToStr(i), true); + } + else{ + aiInterfaces[i]= NULL; + } + } + + //wheather particle systems + if(world.getTileset()->getWeather() == wRainy){ + logger.add("Creating rain particle system", true); + weatherParticleSystem= new RainParticleSystem(); + weatherParticleSystem->setSpeed(12.f/GameConstants::updateFps); + weatherParticleSystem->setPos(gameCamera.getPos()); + renderer.manageParticleSystem(weatherParticleSystem, rsGame); + } + else if(world.getTileset()->getWeather() == wSnowy){ + logger.add("Creating snow particle system", true); + weatherParticleSystem= new SnowParticleSystem(1200); + weatherParticleSystem->setSpeed(1.5f/GameConstants::updateFps); + weatherParticleSystem->setPos(gameCamera.getPos()); + weatherParticleSystem->setTexture(coreData.getSnowTexture()); + renderer.manageParticleSystem(weatherParticleSystem, rsGame); + } + + //init renderer state + if(Socket::enableDebugText) printf("In [%s::%s] Initializing renderer\n",__FILE__,__FUNCTION__); + logger.add("Initializing renderer", true); + renderer.initGame(this); + + //sounds + SoundRenderer &soundRenderer= SoundRenderer::getInstance(); + + Tileset *tileset= world.getTileset(); + AmbientSounds *ambientSounds= tileset->getAmbientSounds(); + + //rain + if(tileset->getWeather()==wRainy && ambientSounds->isEnabledRain()){ + logger.add("Starting ambient stream", true); + soundRenderer.playAmbient(ambientSounds->getRain()); + } + + //snow + if(tileset->getWeather()==wSnowy && ambientSounds->isEnabledSnow()){ + logger.add("Starting ambient stream", true); + soundRenderer.playAmbient(ambientSounds->getSnow()); + } + + if(Socket::enableDebugText) printf("In [%s::%s] Waiting for network\n",__FILE__,__FUNCTION__); + logger.add("Waiting for network", true); + networkManager.getGameNetworkInterface()->waitUntilReady(&checksum); + + if(Socket::enableDebugText) printf("In [%s::%s] Starting music stream\n",__FILE__,__FUNCTION__); + logger.add("Starting music stream", true); + StrSound *gameMusic= world.getThisFaction()->getType()->getMusic(); + soundRenderer.playMusic(gameMusic); + + logger.add("Launching game"); + + if(Socket::enableDebugText) printf("In [%s::%s] END\n",__FILE__,__FUNCTION__); +} + + +// ==================== update ==================== + +//update +void Game::update(){ + + // a) Updates non dependant on speed + + //misc + updateFps++; + mouse2d= (mouse2d+1) % Renderer::maxMouse2dAnim; + + //console + console.update(); + + // b) Updates depandant on speed + + int updateLoops= getUpdateLoops(); + + //update + for(int i=0; igetCpuControl() && scriptManager.getPlayerModifiers(i)->getAiEnabled()){ + aiInterfaces[i]->update(); + } + } + + //World + world.update(); + + // Commander + commander.updateNetwork(); + + //Gui + gui.update(); + + //Particle systems + if(weatherParticleSystem != NULL){ + weatherParticleSystem->setPos(gameCamera.getPos()); + } + renderer.updateParticleManager(rsGame); + } + + //call the chat manager + chatManager.updateNetwork(); + + //check for quiting status + if(NetworkManager::getInstance().getGameNetworkInterface()->getQuit()){ + quitGame(); + } + + //update auto test + if(Config::getInstance().getBool("AutoTest")){ + AutoTest::getInstance().updateGame(this); + } +} + +void Game::updateCamera(){ + gameCamera.update(); +} + + +// ==================== render ==================== + +//render +void Game::render(){ + renderFps++; + render3d(); + render2d(); + Renderer::getInstance().swapBuffers(); +} + +// ==================== tick ==================== + +void Game::tick(){ + lastUpdateFps= updateFps; + lastRenderFps= renderFps; + updateFps= 0; + renderFps= 0; + + //Win/lose check + checkWinner(); + gui.tick(); +} + + +// ==================== events ==================== + +void Game::mouseDownLeft(int x, int y){ + + Map *map= world.getMap(); + const Metrics &metrics= Metrics::getInstance(); + NetworkManager &networkManager= NetworkManager::getInstance(); + bool messageBoxClick= false; + + //scrip message box, only if the exit box is not enabled + if(!mainMessageBox.getEnabled() && scriptManager.getMessageBox()->getEnabled()){ + int button= 1; + if(scriptManager.getMessageBox()->mouseClick(x, y, button)){ + scriptManager.onMessageBoxOk(); + messageBoxClick= true; + } + } + + //minimap panel + if(!messageBoxClick){ + if(metrics.isInMinimap(x, y) && !gui.isSelectingPos()){ + + int xm= x - metrics.getMinimapX(); + int ym= y - metrics.getMinimapY(); + int xCell= static_cast(xm * (static_cast(map->getW()) / metrics.getMinimapW())); + int yCell= static_cast(map->getH() - ym * (static_cast(map->getH()) / metrics.getMinimapH())); + + if(map->isInside(xCell, yCell)){ + if(!gui.isSelectingPos()){ + gameCamera.setPos(Vec2f(static_cast(xCell), static_cast(yCell))); + } + } + } + + //display panel + else if(metrics.isInDisplay(x, y) && !gui.isSelectingPos()){ + int xd= x - metrics.getDisplayX(); + int yd= y - metrics.getDisplayY(); + if(gui.mouseValid(xd, yd)){ + gui.mouseDownLeftDisplay(xd, yd); + } + else{ + gui.mouseDownLeftGraphics(x, y); + } + } + + //graphics panel + else{ + gui.mouseDownLeftGraphics(x, y); + } + } + + //exit message box, has to be the last thing to do in this function + if(mainMessageBox.getEnabled()){ + int button= 1; + if(mainMessageBox.mouseClick(x, y, button)) + { + if(button==1) + { + networkManager.getGameNetworkInterface()->quitGame(true); + quitGame(); + } + else + { + //close message box + mainMessageBox.setEnabled(false); + } + } + } +} + +void Game::mouseDownRight(int x, int y){ + gui.mouseDownRightGraphics(x, y); +} + +void Game::mouseUpLeft(int x, int y){ + gui.mouseUpLeftGraphics(x, y); +} + +void Game::mouseDoubleClickLeft(int x, int y){ + const Metrics &metrics= Metrics::getInstance(); + + //display panel + if(metrics.isInDisplay(x, y) && !gui.isSelectingPos()){ + int xd= x - metrics.getDisplayX(); + int yd= y - metrics.getDisplayY(); + if(gui.mouseValid(xd, yd)){ + return; + } + } + + //graphics panel + gui.mouseDoubleClickLeftGraphics(x, y); +} + +void Game::mouseMove(int x, int y, const MouseState *ms){ + + const Metrics &metrics= Metrics::getInstance(); + + mouseX= x; + mouseY= y; + + //main window + if(y<10){ + gameCamera.setMoveZ(-1); + } + else if(y> metrics.getVirtualH()-10){ + gameCamera.setMoveZ(1); + } + else{ + gameCamera.stopMoveZ(); + } + + if(x<10){ + gameCamera.setMoveX(-1); + } + else if(x> metrics.getVirtualW()-10){ + gameCamera.setMoveX(1); + } + else{ + gameCamera.stopMoveX(); + } + + if(mainMessageBox.getEnabled()){ + mainMessageBox.mouseMove(x, y); + } + + if(scriptManager.getMessageBox()->getEnabled()){ + scriptManager.getMessageBox()->mouseMove(x, y); + } + + //graphics + gui.mouseMoveGraphics(x, y); + + //display + if(metrics.isInDisplay(x, y) && !gui.isSelecting() && !gui.isSelectingPos()){ + if(!gui.isSelectingPos()){ + gui.mouseMoveDisplay(x - metrics.getDisplayX(), y - metrics.getDisplayY()); + } + } +} + +void Game::keyDown(char key){ + + Lang &lang= Lang::getInstance(); + bool speedChangesAllowed= !NetworkManager::getInstance().isNetworkGame(); + + //send ley to the chat manager + chatManager.keyDown(key); + + if(!chatManager.getEditEnabled()){ + + if(key=='N'){ + renderNetworkStatus= true; + } + else if(key=='E'){ + for(int i=0; i<100; ++i){ + string path= "screens/screen" + intToStr(i) + ".tga"; + + FILE *f= fopen(path.c_str(), "rb"); + if(f==NULL){ + Renderer::getInstance().saveScreen(path); + break; + } + else{ + fclose(f); + } + } + } + + //move camera left + else if(key==vkLeft){ + gameCamera.setMoveX(-1); + } + + //move camera right + else if(key==vkRight){ + gameCamera.setMoveX(1); + } + + //move camera up + else if(key==vkUp){ + gameCamera.setMoveZ(1); + } + + //move camera down + else if(key==vkDown){ + gameCamera.setMoveZ(-1); + } + + //change camera mode + else if(key=='F'){ + gameCamera.switchState(); + string stateString= gameCamera.getState()==GameCamera::sGame? lang.get("GameCamera"): lang.get("FreeCamera"); + console.addLine(lang.get("CameraModeSet")+" "+ stateString); + } + + //pause + else if(key=='P'){ + if(speedChangesAllowed){ + if(paused){ + console.addLine(lang.get("GameResumed")); + paused= false; + } + else{ + console.addLine(lang.get("GamePaused")); + paused= true; + } + } + } + + //increment speed + else if(key==vkAdd){ + if(speedChangesAllowed){ + incSpeed(); + } + } + + //decrement speed + else if(key==vkSubtract){ + if(speedChangesAllowed){ + decSpeed(); + } + } + + //exit + else if(key==vkEscape){ + showMessageBox(lang.get("ExitGame?"), "", true); + } + + //group + else if(key>='0' && key<'0'+Selection::maxGroups){ + gui.groupKey(key-'0'); + } + + //hotkeys + if(gameCamera.getState()==GameCamera::sGame){ + gui.hotKey(key); + } + else{ + //rotate camera leftt + if(key=='A'){ + gameCamera.setRotate(-1); + } + + //rotate camera right + else if(key=='D'){ + gameCamera.setRotate(1); + } + + //camera up + else if(key=='S'){ + gameCamera.setMoveY(1); + } + + //camera down + else if(key=='W'){ + gameCamera.setMoveY(-1); + } + } + } +} + +void Game::keyUp(char key){ + + if(!chatManager.getEditEnabled()){ + switch(key){ + case 'N': + renderNetworkStatus= false; + break; + case 'A': + case 'D': + gameCamera.setRotate(0); + break; + + case 'W': + case 'S': + gameCamera.stopMoveY(); + break; + + case vkUp: + case vkDown: + gameCamera.stopMoveZ(); + break; + + case vkLeft: + case vkRight: + gameCamera.stopMoveX(); + break; + } + } +} + +void Game::keyPress(char c){ + chatManager.keyPress(c); +} + +void Game::quitGame(){ + program->setState(new BattleEnd(program, world.getStats())); +} + +// ==================== PRIVATE ==================== + +// ==================== render ==================== + +void Game::render3d(){ + + Renderer &renderer= Renderer::getInstance(); + + //init + renderer.reset3d(); + renderer.computeVisibleQuad(); + renderer.loadGameCameraMatrix(); + renderer.setupLighting(); + + //shadow map + renderer.renderShadowsToTexture(); + + //clear buffers + renderer.clearBuffers(); + + //surface + renderer.renderSurface(); + + //selection circles + renderer.renderSelectionEffects(); + + //units + renderer.renderUnits(); + + //objects + renderer.renderObjects(); + + //water + renderer.renderWater(); + renderer.renderWaterEffects(); + + //particles + renderer.renderParticleManager(rsGame); + + //mouse 3d + renderer.renderMouse3d(); +} + +void Game::render2d(){ + Renderer &renderer= Renderer::getInstance(); + Config &config= Config::getInstance(); + CoreData &coreData= CoreData::getInstance(); + + //init + renderer.reset2d(); + + //display + renderer.renderDisplay(); + + //minimap + if(!config.getBool("PhotoMode")){ + renderer.renderMinimap(); + } + + //selection + renderer.renderSelectionQuad(); + + //exit message box + if(mainMessageBox.getEnabled()){ + renderer.renderMessageBox(&mainMessageBox); + } + + //script message box + if(!mainMessageBox.getEnabled() && scriptManager.getMessageBoxEnabled()){ + renderer.renderMessageBox(scriptManager.getMessageBox()); + } + + //script display text + if(!scriptManager.getDisplayText().empty() && !scriptManager.getMessageBoxEnabled()){ + renderer.renderText( + scriptManager.getDisplayText(), coreData.getMenuFontNormal(), + Vec3f(1.0f), 200, 680, false); + } + + + renderer.renderChatManager(&chatManager); + + //debug info + if(config.getBool("DebugMode")){ + string str; + + str+= "MouseXY: " + intToStr(mouseX) + "," + intToStr(mouseY)+"\n"; + str+= "PosObjWord: " + intToStr(gui.getPosObjWorld().x) + "," + intToStr(gui.getPosObjWorld().y)+"\n"; + str+= "Render FPS: "+intToStr(lastRenderFps)+"\n"; + str+= "Update FPS: "+intToStr(lastUpdateFps)+"\n"; + str+= "GameCamera pos: "+floatToStr(gameCamera.getPos().x)+","+floatToStr(gameCamera.getPos().y)+","+floatToStr(gameCamera.getPos().z)+"\n"; + str+= "Time: "+floatToStr(world.getTimeFlow()->getTime())+"\n"; + str+= "Triangle count: "+intToStr(renderer.getTriangleCount())+"\n"; + str+= "Vertex count: "+intToStr(renderer.getPointCount())+"\n"; + str+= "Frame count:"+intToStr(world.getFrameCount())+"\n"; + + //visible quad + Quad2i visibleQuad= renderer.getVisibleQuad(); + + str+= "Visible quad: "; + for(int i= 0; i<4; ++i){ + str+= "(" + intToStr(visibleQuad.p[i].x) + "," +intToStr(visibleQuad.p[i].y) + ") "; + } + str+= "\n"; + str+= "Visible quad area: " + floatToStr(visibleQuad.area()) +"\n"; + + // resources + for(int i=0; igetResourceTypeCount(); ++j){ + str+= intToStr(world.getFaction(i)->getResource(j)->getAmount()); + str+=" "; + } + str+="\n"; + } + + renderer.renderText( + str, coreData.getMenuFontNormal(), + Vec3f(1.0f), 10, 500, false); + } + + //network status + if(renderNetworkStatus){ + renderer.renderText( + NetworkManager::getInstance().getGameNetworkInterface()->getNetworkStatus(), + coreData.getMenuFontNormal(), + Vec3f(1.0f), 20, 500, false); + } + + //resource info + if(!config.getBool("PhotoMode")){ + renderer.renderResourceStatus(); + renderer.renderConsole(&console); + } + + //2d mouse + renderer.renderMouse2d(mouseX, mouseY, mouse2d, gui.isSelectingPos()? 1.f: 0.f); +} + + +// ==================== misc ==================== + +void Game::checkWinner(){ + if(!gameOver){ + if(gameSettings.getDefaultVictoryConditions()){ + checkWinnerStandard(); + } + else + { + checkWinnerScripted(); + } + } +} + +void Game::checkWinnerStandard(){ + //lose + bool lose= false; + if(!hasBuilding(world.getThisFaction())){ + lose= true; + for(int i=0; iisAlly(world.getThisFaction())){ + world.getStats()->setVictorious(i); + } + } + gameOver= true; + showLoseMessageBox(); + } + + //win + if(!lose){ + bool win= true; + for(int i=0; iisAlly(world.getThisFaction())){ + win= false; + } + } + } + + //if win + if(win){ + for(int i=0; i< world.getFactionCount(); ++i){ + if(world.getFaction(i)->isAlly(world.getThisFaction())){ + world.getStats()->setVictorious(i); + } + } + gameOver= true; + showWinMessageBox(); + } + } +} + +void Game::checkWinnerScripted(){ + if(scriptManager.getGameOver()){ + gameOver= true; + for(int i= 0; igetWinner()){ + world.getStats()->setVictorious(i); + } + } + if(scriptManager.getPlayerModifiers(world.getThisFactionIndex())->getWinner()){ + showWinMessageBox(); + } + else{ + showLoseMessageBox(); + } + } +} + +bool Game::hasBuilding(const Faction *faction){ + for(int i=0; igetUnitCount(); ++i){ + if(faction->getUnit(i)->getType()->hasSkillClass(scBeBuilt)){ + return true; + } + } + return false; +} + +void Game::incSpeed(){ + Lang &lang= Lang::getInstance(); + switch(speed){ + case sSlow: + speed= sNormal; + console.addLine(lang.get("GameSpeedSet")+" "+lang.get("Normal")); + break; + case sNormal: + speed= sFast; + console.addLine(lang.get("GameSpeedSet")+" "+lang.get("Fast")); + break; + default: + break; + } +} + +void Game::decSpeed(){ + Lang &lang= Lang::getInstance(); + switch(speed){ + case sNormal: + speed= sSlow; + console.addLine(lang.get("GameSpeedSet")+" "+lang.get("Slow")); + break; + case sFast: + speed= sNormal; + console.addLine(lang.get("GameSpeedSet")+" "+lang.get("Normal")); + break; + default: + break; + } +} + +int Game::getUpdateLoops(){ + if(paused){ + return 0; + } + else if(speed==sFast){ + return Config::getInstance().getInt("FastSpeedLoops"); + } + else if(speed==sSlow){ + return updateFps % 2 == 0? 1: 0; + } + return 1; +} + +void Game::showLoseMessageBox(){ + Lang &lang= Lang::getInstance(); + showMessageBox(lang.get("YouLose")+", "+lang.get("ExitGame?"), lang.get("BattleOver"), false); +} + +void Game::showWinMessageBox(){ + Lang &lang= Lang::getInstance(); + showMessageBox(lang.get("YouWin")+", "+lang.get("ExitGame?"), lang.get("BattleOver"), false); +} + +void Game::showMessageBox(const string &text, const string &header, bool toggle){ + if(!toggle){ + mainMessageBox.setEnabled(false); + } + + if(!mainMessageBox.getEnabled()){ + mainMessageBox.setText(text); + mainMessageBox.setHeader(header); + mainMessageBox.setEnabled(true); + } + else{ + mainMessageBox.setEnabled(false); + } +} + +}}//end namespace diff --git a/source/glest_game/global/config.cpp b/source/glest_game/global/config.cpp new file mode 100644 index 00000000..bff0409d --- /dev/null +++ b/source/glest_game/global/config.cpp @@ -0,0 +1,89 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "config.h" + +#include "util.h" + +#include "leak_dumper.h" + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Config +// ===================================================== + +Config::Config(){ + properties.load("glest.ini"); +} + +Config &Config::getInstance(){ + static Config config; + return config; +} + +void Config::save(const string &path){ + properties.save(path); +} + +int Config::getInt(const char *key,const char *defaultValueIfNotFound) const { + return properties.getInt(key,defaultValueIfNotFound); +} + +bool Config::getBool(const char *key,const char *defaultValueIfNotFound) const { + return properties.getBool(key,defaultValueIfNotFound); +} + +float Config::getFloat(const char *key,const char *defaultValueIfNotFound) const { + return properties.getFloat(key,defaultValueIfNotFound); +} + +const string Config::getString(const char *key,const char *defaultValueIfNotFound) const { + return properties.getString(key,defaultValueIfNotFound); +} + +int Config::getInt(const string &key,const char *defaultValueIfNotFound) const{ + return properties.getInt(key,defaultValueIfNotFound); +} + +bool Config::getBool(const string &key,const char *defaultValueIfNotFound) const{ + return properties.getBool(key,defaultValueIfNotFound); +} + +float Config::getFloat(const string &key,const char *defaultValueIfNotFound) const{ + return properties.getFloat(key,defaultValueIfNotFound); +} + +const string Config::getString(const string &key,const char *defaultValueIfNotFound) const{ + return properties.getString(key,defaultValueIfNotFound); +} + +void Config::setInt(const string &key, int value){ + properties.setInt(key, value); +} + +void Config::setBool(const string &key, bool value){ + properties.setBool(key, value); +} + +void Config::setFloat(const string &key, float value){ + properties.setFloat(key, value); +} + +void Config::setString(const string &key, const string &value){ + properties.setString(key, value); +} + +string Config::toString(){ + return properties.toString(); +} + +}}// end namespace diff --git a/source/glest_game/global/config.h b/source/glest_game/global/config.h new file mode 100644 index 00000000..9e1330e6 --- /dev/null +++ b/source/glest_game/global/config.h @@ -0,0 +1,58 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_CONFIG_H_ +#define _GLEST_GAME_CONFIG_H_ + +#include "properties.h" + +namespace Glest{ namespace Game{ + +using Shared::Util::Properties; + +// ===================================================== +// class Config +// +// Game configuration +// ===================================================== + +class Config{ +private: + Properties properties; + +private: + Config(); + +public: + static Config &getInstance(); + void save(const string &path="glest.ini"); + + int getInt(const string &key,const char *defaultValueIfNotFound=NULL) const; + bool getBool(const string &key,const char *defaultValueIfNotFound=NULL) const; + float getFloat(const string &key,const char *defaultValueIfNotFound=NULL) const; + const string getString(const string &key,const char *defaultValueIfNotFound=NULL) const; + + int getInt(const char *key,const char *defaultValueIfNotFound=NULL) const; + bool getBool(const char *key,const char *defaultValueIfNotFound=NULL) const; + float getFloat(const char *key,const char *defaultValueIfNotFound=NULL) const; + const string getString(const char *key,const char *defaultValueIfNotFound=NULL) const; + + void setInt(const string &key, int value); + void setBool(const string &key, bool value); + void setFloat(const string &key, float value); + void setString(const string &key, const string &value); + + string toString(); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/graphics/renderer.cpp b/source/glest_game/graphics/renderer.cpp new file mode 100644 index 00000000..2fb6f4d9 --- /dev/null +++ b/source/glest_game/graphics/renderer.cpp @@ -0,0 +1,2892 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Marti�o Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "renderer.h" + +#include "texture_gl.h" +#include "main_menu.h" +#include "config.h" +#include "components.h" +#include "time_flow.h" +#include "graphics_interface.h" +#include "object.h" +#include "core_data.h" +#include "game.h" +#include "metrics.h" +#include "opengl.h" +#include "faction.h" +#include "factory_repository.h" +#include "leak_dumper.h" + +using namespace Shared::Graphics; +using namespace Shared::Graphics::Gl; +using namespace Shared::Util; + +namespace Glest { namespace Game{ + +// ===================================================== +// class MeshCallbackTeamColor +// ===================================================== + +class MeshCallbackTeamColor: public MeshCallback{ +private: + const Texture *teamTexture; + +public: + void setTeamTexture(const Texture *teamTexture) {this->teamTexture= teamTexture;} + virtual void execute(const Mesh *mesh); +}; + +void MeshCallbackTeamColor::execute(const Mesh *mesh){ + + //team color + if(mesh->getCustomTexture() && teamTexture!=NULL){ + //texture 0 + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + + //set color to interpolation + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); + + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE1); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA); + + //set alpha to 1 + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + + //texture 1 + glActiveTexture(GL_TEXTURE1); + glMultiTexCoord2f(GL_TEXTURE1, 0.f, 0.f); + glEnable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, static_cast(teamTexture)->getHandle()); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + //set alpha to 1 + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + + glActiveTexture(GL_TEXTURE0); + } + else{ + glActiveTexture(GL_TEXTURE1); + glDisable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + } +} + +// =========================================================== +// class Renderer +// =========================================================== + +// ===================== PUBLIC ======================== + +const int Renderer::maxProgressBar= 100; +const Vec4f Renderer::progressBarBack1= Vec4f(0.7f, 0.7f, 0.7f, 0.7f); +const Vec4f Renderer::progressBarBack2= Vec4f(0.7f, 0.7f, 0.7f, 1.f); +const Vec4f Renderer::progressBarFront1= Vec4f(0.f, 0.5f, 0.f, 1.f); +const Vec4f Renderer::progressBarFront2= Vec4f(0.f, 0.1f, 0.f, 1.f); + +const float Renderer::sunDist= 10e6; +const float Renderer::moonDist= 10e6; +const float Renderer::lightAmbFactor= 0.4f; + +const int Renderer::maxMouse2dAnim= 100; + +const GLenum Renderer::baseTexUnit= GL_TEXTURE0; +const GLenum Renderer::fowTexUnit= GL_TEXTURE1; +const GLenum Renderer::shadowTexUnit= GL_TEXTURE2; + +const float Renderer::selectionCircleRadius= 0.7f; +const float Renderer::magicCircleRadius= 1.f; + +//perspective values +const float Renderer::perspFov= 60.f; +const float Renderer::perspNearPlane= 1.f; +const float Renderer::perspFarPlane= 50.f; + +const float Renderer::ambFactor= 0.7f; +const Vec4f Renderer::fowColor= Vec4f(0.0f, 0.0f, 0.0f, 1.0f); +const Vec4f Renderer::defSpecularColor= Vec4f(0.8f, 0.8f, 0.8f, 1.f); +const Vec4f Renderer::defDiffuseColor= Vec4f(1.f, 1.f, 1.f, 1.f); +const Vec4f Renderer::defAmbientColor= Vec4f(1.f * ambFactor, 1.f * ambFactor, 1.f * ambFactor, 1.f); +const Vec4f Renderer::defColor= Vec4f(1.f, 1.f, 1.f, 1.f); + +const float Renderer::maxLightDist= 50.f; + +// ==================== constructor and destructor ==================== + +Renderer::Renderer(){ + GraphicsInterface &gi= GraphicsInterface::getInstance(); + FactoryRepository &fr= FactoryRepository::getInstance(); + Config &config= Config::getInstance(); + + gi.setFactory(fr.getGraphicsFactory(config.getString("FactoryGraphics"))); + GraphicsFactory *graphicsFactory= GraphicsInterface::getInstance().getFactory(); + + modelRenderer= graphicsFactory->newModelRenderer(); + textRenderer= graphicsFactory->newTextRenderer2D(); + particleRenderer= graphicsFactory->newParticleRenderer(); + + //resources + for(int i=0; inewModelManager(); + textureManager[i]= graphicsFactory->newTextureManager(); + modelManager[i]->setTextureManager(textureManager[i]); + particleManager[i]= graphicsFactory->newParticleManager(); + fontManager[i]= graphicsFactory->newFontManager(); + } + + allowRotateUnits = config.getBool("AllowRotateUnits","0"); +} + +Renderer::~Renderer(){ + delete modelRenderer; + delete textRenderer; + delete particleRenderer; + + //resources + for(int i=0; iinit(); + textureManager[rsGlobal]->init(); + fontManager[rsGlobal]->init(); + + init2dList(); +} + +void Renderer::initGame(Game *game){ + this->game= game; + + //check gl caps + checkGlOptionalCaps(); + + //vars + shadowMapFrame= 0; + waterAnim= 0; + + //shadows + if(shadows==sProjected || shadows==sShadowMapping){ + static_cast(modelRenderer)->setSecondaryTexCoordUnit(2); + + glGenTextures(1, &shadowMapHandle); + glBindTexture(GL_TEXTURE_2D, shadowMapHandle); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + if(shadows==sShadowMapping){ + + //shadow mapping + glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FAIL_VALUE_ARB, 1.0f-shadowAlpha); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, + shadowTextureSize, shadowTextureSize, + 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); + } + else{ + + //projected + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, + shadowTextureSize, shadowTextureSize, + 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL); + } + + shadowMapFrame= -1; + } + + //texture init + modelManager[rsGame]->init(); + textureManager[rsGame]->init(); + fontManager[rsGame]->init(); + + init3dList(); +} + +void Renderer::initMenu(MainMenu *mm){ + modelManager[rsMenu]->init(); + textureManager[rsMenu]->init(); + fontManager[rsMenu]->init(); + //modelRenderer->setCustomTexture(CoreData::getInstance().getCustomTexture()); + + init3dListMenu(mm); +} + +void Renderer::reset3d(){ + assertGl(); + glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR); + glCallList(list3d); + pointCount= 0; + triangleCount= 0; + assertGl(); +} + +void Renderer::reset2d(){ + assertGl(); + glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SINGLE_COLOR); + glCallList(list2d); + assertGl(); +} + +void Renderer::reset3dMenu(){ + assertGl(); + glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SINGLE_COLOR); + glCallList(list3dMenu); + assertGl(); +} + +// ==================== end ==================== + +void Renderer::end(){ + + //delete resources + modelManager[rsGlobal]->end(); + textureManager[rsGlobal]->end(); + fontManager[rsGlobal]->end(); + particleManager[rsGlobal]->end(); + + //delete 2d list + glDeleteLists(list2d, 1); +} + +void Renderer::endGame(){ + game= NULL; + + //delete resources + modelManager[rsGame]->end(); + textureManager[rsGame]->end(); + fontManager[rsGame]->end(); + particleManager[rsGame]->end(); + + if(shadows==sProjected || shadows==sShadowMapping){ + glDeleteTextures(1, &shadowMapHandle); + } + + glDeleteLists(list3d, 1); +} + +void Renderer::endMenu(){ + //delete resources + modelManager[rsMenu]->end(); + textureManager[rsMenu]->end(); + fontManager[rsMenu]->end(); + particleManager[rsMenu]->end(); + + glDeleteLists(list3dMenu, 1); +} + +void Renderer::reloadResources(){ + for(int i=0; iend(); + textureManager[i]->end(); + fontManager[i]->end(); + } + for(int i=0; iinit(); + textureManager[i]->init(); + fontManager[i]->init(); + } +} + + +// ==================== engine interface ==================== + +Model *Renderer::newModel(ResourceScope rs){ + return modelManager[rs]->newModel(); +} + +Texture2D *Renderer::newTexture2D(ResourceScope rs){ + return textureManager[rs]->newTexture2D(); +} + +Texture3D *Renderer::newTexture3D(ResourceScope rs){ + return textureManager[rs]->newTexture3D(); +} + +Font2D *Renderer::newFont(ResourceScope rs){ + return fontManager[rs]->newFont2D(); +} + +void Renderer::manageParticleSystem(ParticleSystem *particleSystem, ResourceScope rs){ + particleManager[rs]->manage(particleSystem); +} + +void Renderer::updateParticleManager(ResourceScope rs){ + particleManager[rs]->update(); +} + +void Renderer::renderParticleManager(ResourceScope rs){ + glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + glDepthFunc(GL_LESS); + particleRenderer->renderManager(particleManager[rs], modelRenderer); + glPopAttrib(); +} + +void Renderer::swapBuffers(){ + glFlush(); + GraphicsInterface::getInstance().getCurrentContext()->swapBuffers(); +} + +// ==================== lighting ==================== + +//places all the opengl lights +void Renderer::setupLighting(){ + + int lightCount= 0; + const World *world= game->getWorld(); + const GameCamera *gameCamera= game->getGameCamera(); + const TimeFlow *timeFlow= world->getTimeFlow(); + float time= timeFlow->getTime(); + + assertGl(); + + //sun/moon light + Vec3f lightColor= computeLightColor(time); + Vec3f fogColor= world->getTileset()->getFogColor(); + Vec4f lightPos= timeFlow->isDay()? computeSunPos(time): computeMoonPos(time); + nearestLightPos= lightPos; + + glLightfv(GL_LIGHT0, GL_POSITION, lightPos.ptr()); + glLightfv(GL_LIGHT0, GL_AMBIENT, Vec4f(lightColor*lightAmbFactor, 1.f).ptr()); + glLightfv(GL_LIGHT0, GL_DIFFUSE, Vec4f(lightColor, 1.f).ptr()); + glLightfv(GL_LIGHT0, GL_SPECULAR, Vec4f(0.0f, 0.0f, 0.f, 1.f).ptr()); + + glFogfv(GL_FOG_COLOR, Vec4f(fogColor*lightColor, 1.f).ptr()); + + lightCount++; + + //disable all secondary lights + for(int i= 1; iisTotalNight()){ + for(int i=0; igetFactionCount() && lightCountgetFaction(i)->getUnitCount() && lightCountgetFaction(i)->getUnit(j); + if(world->toRenderUnit(unit) && + unit->getCurrVector().dist(gameCamera->getPos())getType()->getLight() && unit->isOperative()){ + + Vec4f pos= Vec4f(unit->getCurrVector()); + pos.y+=4.f; + + GLenum lightEnum= GL_LIGHT0 + lightCount; + + glEnable(lightEnum); + glLightfv(lightEnum, GL_POSITION, pos.ptr()); + glLightfv(lightEnum, GL_AMBIENT, Vec4f(unit->getType()->getLightColor()).ptr()); + glLightfv(lightEnum, GL_DIFFUSE, Vec4f(unit->getType()->getLightColor()).ptr()); + glLightfv(lightEnum, GL_SPECULAR, Vec4f(unit->getType()->getLightColor()*0.3f).ptr()); + glLightf(lightEnum, GL_QUADRATIC_ATTENUATION, 0.05f); + + ++lightCount; + + const GameCamera *gameCamera= game->getGameCamera(); + + if(Vec3f(pos).dist(gameCamera->getPos())getPos())){ + nearestLightPos= pos; + } + } + } + } + } + + assertGl(); +} + +void Renderer::loadGameCameraMatrix(){ + const GameCamera *gameCamera= game->getGameCamera(); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glRotatef(gameCamera->getVAng(), -1, 0, 0); + glRotatef(gameCamera->getHAng(), 0, 1, 0); + glTranslatef(-gameCamera->getPos().x, -gameCamera->getPos().y, -gameCamera->getPos().z); +} + +void Renderer::loadCameraMatrix(const Camera *camera){ + Vec3f position= camera->getPosition(); + Quaternion orientation= camera->getOrientation().conjugate(); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glMultMatrixf(orientation.toMatrix4().ptr()); + glTranslatef(-position.x, -position.y, -position.z); +} + +void Renderer::computeVisibleQuad(){ + const GameCamera *gameCamera= game->getGameCamera(); + visibleQuad= gameCamera->computeVisibleQuad(); +} + +// ======================================= +// basic rendering +// ======================================= + +void Renderer::renderMouse2d(int x, int y, int anim, float fade){ + float color1, color2; + + float fadeFactor= fade+1.f; + + anim= anim*2-maxMouse2dAnim; + + color2= (abs(anim*fadeFactor)/static_cast(maxMouse2dAnim))/2.f+0.4f; + color1= (abs(anim*fadeFactor)/static_cast(maxMouse2dAnim))/2.f+0.8f; + + glPushAttrib(GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT | GL_LINE_BIT); + glEnable(GL_BLEND); + + //inside + glColor4f(0.4f*fadeFactor, 0.2f*fadeFactor, 0.2f*fadeFactor, 0.5f*fadeFactor); + glBegin(GL_TRIANGLES); + glVertex2i(x, y); + glVertex2i(x+20, y-10); + glVertex2i(x+10, y-20); + glEnd(); + + //biorder + glLineWidth(2); + glBegin(GL_LINE_LOOP); + glColor4f(1.f, 0.2f, 0, color1); + glVertex2i(x, y); + glColor4f(1.f, 0.4f, 0, color2); + glVertex2i(x+20, y-10); + glColor4f(1.f, 0.4f, 0, color2); + glVertex2i(x+10, y-20); + glEnd(); + glPopAttrib(); +} + +void Renderer::renderMouse3d(){ + + const Gui *gui= game->getGui(); + const Mouse3d *mouse3d= gui->getMouse3d(); + const Map *map= game->getWorld()->getMap(); + + GLUquadricObj *cilQuadric; + Vec4f color; + + assertGl(); + + if((mouse3d->isEnabled() || gui->isPlacingBuilding()) && gui->isValidPosObjWorld()){ + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glPushAttrib(GL_CURRENT_BIT | GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT); + glEnable(GL_BLEND); + glDisable(GL_STENCIL_TEST); + glDepthFunc(GL_LESS); + glEnable(GL_COLOR_MATERIAL); + glDepthMask(GL_FALSE); + + Vec2i pos= gui->getPosObjWorld(); + Vec3f pos3f= Vec3f(pos.x, map->getCell(pos)->getHeight(), pos.y); + + if(gui->isPlacingBuilding()){ + + const UnitType *building= gui->getBuilding(); + + //selection building emplacement + float offset= building->getSize()/2.f-0.5f; + glTranslatef(pos3f.x+offset, pos3f.y, pos3f.z+offset); + + //choose color + if(map->isFreeCells(pos, building->getSize(), fLand)){ + color= Vec4f(1.f, 1.f, 1.f, 0.5f); + } + else{ + color= Vec4f(1.f, 0.f, 0.f, 0.5f); + } + + modelRenderer->begin(true, true, false); + glColor4fv(color.ptr()); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color.ptr()); + const Model *buildingModel= building->getFirstStOfClass(scStop)->getAnimation(); + + //!!! + if(allowRotateUnits == true) { + int factionIndex = game->getWorld()->getThisFactionIndex(); + char unitKey[50]=""; + sprintf(unitKey,"%d_%d",building->getId(),factionIndex); + + float rotateAmount = gui->getUnitTypeBuildRotation(unitKey); + if(rotateAmount > 0) { + //if(Socket::enableDebugText) printf("In [%s::%s] rotate unit id = %d amount = %f\n",__FILE__,__FUNCTION__,building->getId(),rotateAmount); + glRotatef(rotateAmount, 0.f, 1.f, 0.f); + } + } + buildingModel->updateInterpolationData(0.f, false); + modelRenderer->render(buildingModel); + glDisable(GL_COLOR_MATERIAL); + modelRenderer->end(); + + } + else{ + //standard mouse + glDisable(GL_TEXTURE_2D); + glDisable(GL_CULL_FACE); + color= Vec4f(1.f, 0.f, 0.f, 1.f-mouse3d->getFade()); + glColor4fv(color.ptr()); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color.ptr()); + + glTranslatef(pos3f.x, pos3f.y+2.f, pos3f.z); + glRotatef(90.f, 1.f, 0.f, 0.f); + glRotatef(static_cast(mouse3d->getRot()), 0.f, 0.f, 1.f); + + cilQuadric= gluNewQuadric(); + gluQuadricDrawStyle(cilQuadric, GLU_FILL); + gluCylinder(cilQuadric, 0.5f, 0.f, 2.f, 4, 1); + gluCylinder(cilQuadric, 0.5f, 0.f, 0.f, 4, 1); + glTranslatef(0.f, 0.f, 1.f); + gluCylinder(cilQuadric, 0.7f, 0.f, 1.f, 4, 1); + gluCylinder(cilQuadric, 0.7f, 0.f, 0.f, 4, 1); + gluDeleteQuadric(cilQuadric); + } + + glPopAttrib(); + glPopMatrix(); + } + +} + +void Renderer::renderBackground(const Texture2D *texture){ + + const Metrics &metrics= Metrics::getInstance(); + + assertGl(); + + glPushAttrib(GL_ENABLE_BIT); + + glDisable(GL_LIGHTING); + glEnable(GL_TEXTURE_2D); + + renderQuad(0, 0, metrics.getVirtualW(), metrics.getVirtualH(), texture); + + glPopAttrib(); + + assertGl(); +} + +void Renderer::renderTextureQuad(int x, int y, int w, int h, const Texture2D *texture, float alpha){ + assertGl(); + + glPushAttrib(GL_ENABLE_BIT); + + glDisable(GL_LIGHTING); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + + glColor4f(1.f, 1.f, 1.f, alpha); + renderQuad(x, y, w, h, texture); + + glPopAttrib(); + + assertGl(); +} + +void Renderer::renderConsole(const Console *console){ + glPushAttrib(GL_ENABLE_BIT); + glEnable(GL_BLEND); + + for(int i=0; igetLineCount(); ++i){ + renderTextShadow( + console->getLine(i), + CoreData::getInstance().getConsoleFont(), + 20, i*20+20); + } + + glPopAttrib(); +} + +void Renderer::renderChatManager(const ChatManager *chatManager){ + Lang &lang= Lang::getInstance(); + + if(chatManager->getEditEnabled()){ + string text; + + if(chatManager->getTeamMode()){ + text+= lang.get("Team"); + } + else + { + text+= lang.get("All"); + } + text+= ": " + chatManager->getText() + "_"; + + textRenderer->begin(CoreData::getInstance().getConsoleFont()); + textRenderer->render(text, 300, 150); + textRenderer->end(); + } +} + +void Renderer::renderResourceStatus(){ + + const Metrics &metrics= Metrics::getInstance(); + const World *world= game->getWorld(); + const Faction *thisFaction= world->getFaction(world->getThisFactionIndex()); + + assertGl(); + + glPushAttrib(GL_ENABLE_BIT); + + int j= 0; + for(int i= 0; igetTechTree()->getResourceTypeCount(); ++i){ + const ResourceType *rt= world->getTechTree()->getResourceType(i); + const Resource *r= thisFaction->getResource(rt); + + //if any unit produces the resource + bool showResource= false; + for(int k=0; kgetType()->getUnitTypeCount(); ++k){ + const UnitType *ut= thisFaction->getType()->getUnitType(k); + if(ut->getCost(rt)!=NULL){ + showResource= true; + break; + } + } + + //draw resource status + if(showResource){ + + string str= intToStr(r->getAmount()); + + glEnable(GL_TEXTURE_2D); + renderQuad(j*100+200, metrics.getVirtualH()-30, 16, 16, rt->getImage()); + + if(rt->getClass() != rcStatic) + { + str+= "/" + intToStr(thisFaction->getStoreAmount(rt)); + } + if(rt->getClass()==rcConsumable){ + str+= "("; + if(r->getBalance()>0){ + str+= "+"; + } + str+= intToStr(r->getBalance()) + ")"; + } + + glDisable(GL_TEXTURE_2D); + + renderTextShadow( + str, CoreData::getInstance().getMenuFontSmall(), + j*100+220, metrics.getVirtualH()-30, false); + ++j; + } + + } + + glPopAttrib(); + + assertGl(); +} + +void Renderer::renderSelectionQuad(){ + + const Gui *gui= game->getGui(); + const SelectionQuad *sq= gui->getSelectionQuad(); + + Vec2i down= sq->getPosDown(); + Vec2i up= sq->getPosUp(); + + if(gui->isSelecting()){ + glPushAttrib(GL_CURRENT_BIT | GL_LINE_BIT); + glColor3f(0,1,0); + glBegin(GL_LINE_LOOP); + glVertex2i(down.x, down.y); + glVertex2i(up.x, down.y); + glVertex2i(up.x, up.y); + glVertex2i(down.x, up.y); + glEnd(); + glPopAttrib(); + } +} + +Vec2i computeCenteredPos(const string &text, const Font2D *font, int x, int y){ + Vec2i textPos; + + const Metrics &metrics= Metrics::getInstance(); + const FontMetrics *fontMetrics= font->getMetrics(); + + textPos= Vec2i( + x-metrics.toVirtualX(static_cast(fontMetrics->getTextWidth(text)/2.f)), + y-metrics.toVirtualY(static_cast(fontMetrics->getHeight()/2.f))); + + return textPos; +} + +void Renderer::renderText(const string &text, const Font2D *font, float alpha, int x, int y, bool centered){ + glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT); + glEnable(GL_BLEND); + glColor4fv(Vec4f(1.f, 1.f, 1.f, alpha).ptr()); + + Vec2i pos= centered? computeCenteredPos(text, font, x, y): Vec2i(x, y); + + textRenderer->begin(font); + textRenderer->render(text, pos.x, pos.y); + textRenderer->end(); + + glPopAttrib(); +} + +void Renderer::renderText(const string &text, const Font2D *font, const Vec3f &color, int x, int y, bool centered){ + glPushAttrib(GL_CURRENT_BIT); + glColor3fv(color.ptr()); + + Vec2i pos= centered? computeCenteredPos(text, font, x, y): Vec2i(x, y); + + textRenderer->begin(font); + textRenderer->render(text, pos.x, pos.y); + textRenderer->end(); + + glPopAttrib(); +} + +void Renderer::renderTextShadow(const string &text, const Font2D *font, int x, int y, bool centered){ + glPushAttrib(GL_CURRENT_BIT); + + Vec2i pos= centered? computeCenteredPos(text, font, x, y): Vec2i(x, y); + + textRenderer->begin(font); + glColor3f(0.0f, 0.0f, 0.0f); + textRenderer->render(text, pos.x-1.0f, pos.y-1.0f); + glColor3f(1.0f, 1.0f, 1.0f); + textRenderer->render(text, pos.x, pos.y); + textRenderer->end(); + + glPopAttrib(); +} + +// ============= COMPONENTS ============================= + +void Renderer::renderLabel(const GraphicLabel *label){ + + glPushAttrib(GL_ENABLE_BIT); + glEnable(GL_BLEND); + + Vec2i textPos; + int x= label->getX(); + int y= label->getY(); + int h= label->getH(); + int w= label->getW(); + + if(label->getCentered()){ + textPos= Vec2i(x+w/2, y+h/2); + } + else{ + textPos= Vec2i(x, y+h/4); + } + + renderText(label->getText(), label->getFont(), GraphicComponent::getFade(), textPos.x, textPos.y, label->getCentered()); + + glPopAttrib(); +} + +void Renderer::renderButton(const GraphicButton *button){ + + int x= button->getX(); + int y= button->getY(); + int h= button->getH(); + int w= button->getW(); + + glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT); + + //background + CoreData &coreData= CoreData::getInstance(); + Texture2D *backTexture= w>3*h/2? coreData.getButtonBigTexture(): coreData.getButtonSmallTexture(); + + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + + glBindTexture(GL_TEXTURE_2D, static_cast(backTexture)->getHandle()); + + //button + Vec4f color= Vec4f(1.f, 1.f, 1.f, GraphicComponent::getFade()); + glColor4fv(color.ptr()); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(0.f, 0.f); + glVertex2f(x, y); + + glTexCoord2f(0.f, 1.f); + glVertex2f(x, y+h); + + glTexCoord2f(1.f, 0.f); + glVertex2f(x+w, y); + + glTexCoord2f(1.f, 1.f); + glVertex2f(x+w, y+h); + + glEnd(); + + glDisable(GL_TEXTURE_2D); + + //lighting + float anim= GraphicComponent::getAnim(); + if(anim>0.5f) anim= 1.f-anim; + + if(button->getLighted()){ + const int lightSize= 0; + const Vec4f color1= Vec4f(1.f, 1.f, 1.f, 0.1f+anim*0.5f); + const Vec4f color2= Vec4f(1.f, 1.f, 1.f, 0.3f+anim); + + glBegin(GL_TRIANGLE_FAN); + + glColor4fv(color2.ptr()); + glVertex2f(x+w/2, y+h/2); + + glColor4fv(color1.ptr()); + glVertex2f(x-lightSize, y-lightSize); + + glColor4fv(color1.ptr()); + glVertex2f(x+w+lightSize, y-lightSize); + + glColor4fv(color1.ptr()); + glVertex2f(x+w+lightSize, y+h+lightSize); + + glColor4fv(color1.ptr()); + glVertex2f(x+w+lightSize, y+h+lightSize); + + glColor4fv(color1.ptr()); + glVertex2f(x-lightSize, y+h+lightSize); + + glColor4fv(color1.ptr()); + glVertex2f(x-lightSize, y-lightSize); + + glEnd(); + } + + Vec2i textPos= Vec2i(x+w/2, y+h/2); + + renderText( + button->getText(), button->getFont(), GraphicButton::getFade(), + x+w/2, y+h/2, true); + + glPopAttrib(); +} + +void Renderer::renderListBox(const GraphicListBox *listBox){ + + renderButton(listBox->getButton1()); + renderButton(listBox->getButton2()); + + glPushAttrib(GL_ENABLE_BIT); + glEnable(GL_BLEND); + + GraphicLabel label; + label.init(listBox->getX(), listBox->getY(), listBox->getW(), listBox->getH(), true); + label.setText(listBox->getText()); + label.setFont(listBox->getFont()); + renderLabel(&label); + + glPopAttrib(); +} + +void Renderer::renderMessageBox(const GraphicMessageBox *messageBox){ + + //background + glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT); + glEnable(GL_BLEND); + + glColor4f(0.0f, 0.0f, 0.0f, 0.5f) ; + glBegin(GL_TRIANGLE_STRIP); + glVertex2i(messageBox->getX(), messageBox->getY()+9*messageBox->getH()/10); + glVertex2i(messageBox->getX(), messageBox->getY()); + glVertex2i(messageBox->getX() + messageBox->getW(), messageBox->getY() + 9*messageBox->getH()/10); + glVertex2i(messageBox->getX() + messageBox->getW(), messageBox->getY()); + glEnd(); + + glColor4f(0.0f, 0.0f, 0.0f, 0.8f) ; + glBegin(GL_TRIANGLE_STRIP); + glVertex2i(messageBox->getX(), messageBox->getY()+messageBox->getH()); + glVertex2i(messageBox->getX(), messageBox->getY()+9*messageBox->getH()/10); + glVertex2i(messageBox->getX() + messageBox->getW(), messageBox->getY() + messageBox->getH()); + glVertex2i(messageBox->getX() + messageBox->getW(), messageBox->getY()+9*messageBox->getH()/10); + glEnd(); + + glBegin(GL_LINE_LOOP); + glColor4f(0.5f, 0.5f, 0.5f, 0.25f) ; + glVertex2i(messageBox->getX(), messageBox->getY()); + + glColor4f(0.0f, 0.0f, 0.0f, 0.25f) ; + glVertex2i(messageBox->getX()+ messageBox->getW(), messageBox->getY()); + + glColor4f(0.5f, 0.5f, 0.5f, 0.25f) ; + glVertex2i(messageBox->getX()+ messageBox->getW(), messageBox->getY() + messageBox->getH()); + + glColor4f(0.25f, 0.25f, 0.25f, 0.25f) ; + glVertex2i(messageBox->getX(), messageBox->getY() + messageBox->getH()); + glEnd(); + + glBegin(GL_LINE_STRIP); + glColor4f(1.0f, 1.0f, 1.0f, 0.25f) ; + glVertex2i(messageBox->getX(), messageBox->getY() + 90*messageBox->getH()/100); + + glColor4f(0.5f, 0.5f, 0.5f, 0.25f) ; + glVertex2i(messageBox->getX()+ messageBox->getW(), messageBox->getY() + 90*messageBox->getH()/100); + glEnd(); + + glPopAttrib(); + + //buttons + renderButton(messageBox->getButton1()); + if(messageBox->getButtonCount()==2){ + renderButton(messageBox->getButton2()); + } + + //text + renderText( + messageBox->getText(), messageBox->getFont(), Vec3f(1.0f, 1.0f, 1.0f), + messageBox->getX()+15, messageBox->getY()+7*messageBox->getH()/10, + false ); + + renderText( + messageBox->getHeader(), messageBox->getFont(),Vec3f(1.0f, 1.0f, 1.0f), + messageBox->getX()+15, messageBox->getY()+93*messageBox->getH()/100, + false ); +} + +// ==================== complex rendering ==================== + +void Renderer::renderSurface(){ + + int lastTex=-1; + int currTex; + const World *world= game->getWorld(); + const Map *map= world->getMap(); + const Rect2i mapBounds(0, 0, map->getSurfaceW()-1, map->getSurfaceH()-1); + float coordStep= world->getTileset()->getSurfaceAtlas()->getCoordStep(); + + assertGl(); + + const Texture2D *fowTex= world->getMinimap()->getFowTexture(); + + glPushAttrib(GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_FOG_BIT | GL_TEXTURE_BIT); + + glEnable(GL_BLEND); + glEnable(GL_COLOR_MATERIAL); + glDisable(GL_ALPHA_TEST); + + //fog of war tex unit + glActiveTexture(fowTexUnit); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, static_cast(fowTex)->getHandle()); + glTexSubImage2D( + GL_TEXTURE_2D, 0, 0, 0, + fowTex->getPixmap()->getW(), fowTex->getPixmap()->getH(), + GL_ALPHA, GL_UNSIGNED_BYTE, fowTex->getPixmap()->getPixels()); + + //shadow texture + if(shadows==sProjected || shadows==sShadowMapping){ + glActiveTexture(shadowTexUnit); + glEnable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, shadowMapHandle); + + static_cast(modelRenderer)->setDuplicateTexCoords(true); + enableProjectiveTexturing(); + } + + glActiveTexture(baseTexUnit); + + Quad2i scaledQuad= visibleQuad/Map::cellScale; + + PosQuadIterator pqi(map, scaledQuad); + while(pqi.next()){ + + const Vec2i &pos= pqi.getPos(); + + if(mapBounds.isInside(pos)){ + + SurfaceCell *tc00= map->getSurfaceCell(pos.x, pos.y); + SurfaceCell *tc10= map->getSurfaceCell(pos.x+1, pos.y); + SurfaceCell *tc01= map->getSurfaceCell(pos.x, pos.y+1); + SurfaceCell *tc11= map->getSurfaceCell(pos.x+1, pos.y+1); + + triangleCount+= 2; + pointCount+= 4; + + //set texture + currTex= static_cast(tc00->getSurfaceTexture())->getHandle(); + if(currTex!=lastTex){ + lastTex=currTex; + glBindTexture(GL_TEXTURE_2D, lastTex); + } + + Vec2f surfCoord= tc00->getSurfTexCoord(); + + glBegin(GL_TRIANGLE_STRIP); + + //draw quad using immediate mode + glMultiTexCoord2fv(fowTexUnit, tc01->getFowTexCoord().ptr()); + glMultiTexCoord2f(baseTexUnit, surfCoord.x, surfCoord.y+coordStep); + glNormal3fv(tc01->getNormal().ptr()); + glVertex3fv(tc01->getVertex().ptr()); + + glMultiTexCoord2fv(fowTexUnit, tc00->getFowTexCoord().ptr()); + glMultiTexCoord2f(baseTexUnit, surfCoord.x, surfCoord.y); + glNormal3fv(tc00->getNormal().ptr()); + glVertex3fv(tc00->getVertex().ptr()); + + glMultiTexCoord2fv(fowTexUnit, tc11->getFowTexCoord().ptr()); + glMultiTexCoord2f(baseTexUnit, surfCoord.x+coordStep, surfCoord.y+coordStep); + glNormal3fv(tc11->getNormal().ptr()); + glVertex3fv(tc11->getVertex().ptr()); + + glMultiTexCoord2fv(fowTexUnit, tc10->getFowTexCoord().ptr()); + glMultiTexCoord2f(baseTexUnit, surfCoord.x+coordStep, surfCoord.y); + glNormal3fv(tc10->getNormal().ptr()); + glVertex3fv(tc10->getVertex().ptr()); + + glEnd(); + } + } + glEnd(); + + //Restore + static_cast(modelRenderer)->setDuplicateTexCoords(false); + glPopAttrib(); + + //assert + glGetError(); //remove when first mtex problem solved + assertGl(); +} + +void Renderer::renderObjects(){ + const World *world= game->getWorld(); + const Map *map= world->getMap(); + + assertGl(); + const Texture2D *fowTex= world->getMinimap()->getFowTexture(); + Vec3f baseFogColor= world->getTileset()->getFogColor()*computeLightColor(world->getTimeFlow()->getTime()); + + glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_FOG_BIT | GL_LIGHTING_BIT | GL_TEXTURE_BIT); + + if(shadows==sShadowMapping){ + glActiveTexture(shadowTexUnit); + glEnable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, shadowMapHandle); + + static_cast(modelRenderer)->setDuplicateTexCoords(true); + enableProjectiveTexturing(); + } + + glActiveTexture(baseTexUnit); + + glEnable(GL_COLOR_MATERIAL); + glAlphaFunc(GL_GREATER, 0.5f); + + modelRenderer->begin(true, true, false); + int thisTeamIndex= world->getThisTeamIndex(); + + PosQuadIterator pqi(map, visibleQuad, Map::cellScale); + while(pqi.next()){ + const Vec2i pos= pqi.getPos(); + + if(map->isInside(pos)){ + + SurfaceCell *sc= map->getSurfaceCell(Map::toSurfCoords(pos)); + Object *o= sc->getObject(); + if(sc->isExplored(thisTeamIndex) && o!=NULL){ + + const Model *objModel= sc->getObject()->getModel(); + Vec3f v= o->getPos(); + + //ambient and diffuse color is taken from cell color + float fowFactor= fowTex->getPixmap()->getPixelf(pos.x/Map::cellScale, pos.y/Map::cellScale); + Vec4f color= Vec4f(Vec3f(fowFactor), 1.f); + glColor4fv(color.ptr()); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (color*ambFactor).ptr()); + glFogfv(GL_FOG_COLOR, (baseFogColor*fowFactor).ptr()); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glTranslatef(v.x, v.y, v.z); + glRotatef(o->getRotation(), 0.f, 1.f, 0.f); + + objModel->updateInterpolationData(0.f, true); + modelRenderer->render(objModel); + + triangleCount+= objModel->getTriangleCount(); + pointCount+= objModel->getVertexCount(); + + glPopMatrix(); + + } + } + } + + modelRenderer->end(); + + //restore + static_cast(modelRenderer)->setDuplicateTexCoords(true); + glPopAttrib(); +} + +void Renderer::renderWater(){ + + bool closed= false; + const World *world= game->getWorld(); + const Map *map= world->getMap(); + + float waterAnim= world->getWaterEffects()->getAmin(); + + //assert + assertGl(); + + glPushAttrib(GL_TEXTURE_BIT | GL_ENABLE_BIT | GL_CURRENT_BIT); + + //water texture nit + glDisable(GL_TEXTURE_2D); + + glEnable(GL_BLEND); + if(textures3D){ + Texture3D *waterTex= world->getTileset()->getWaterTex(); + glEnable(GL_TEXTURE_3D); + glBindTexture(GL_TEXTURE_3D, static_cast(waterTex)->getHandle()); + } + else{ + glEnable(GL_COLOR_MATERIAL); + glColor4f(0.5f, 0.5f, 1.0f, 0.5f); + glBindTexture(GL_TEXTURE_3D, 0); + } + + assertGl(); + + //fog of War texture Unit + const Texture2D *fowTex= world->getMinimap()->getFowTexture(); + glActiveTexture(fowTexUnit); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, static_cast(fowTex)->getHandle()); + glActiveTexture(baseTexUnit); + + assertGl(); + + Rect2i boundingRect= visibleQuad.computeBoundingRect(); + Rect2i scaledRect= boundingRect/Map::cellScale; + scaledRect.clamp(0, 0, map->getSurfaceW()-1, map->getSurfaceH()-1); + + float waterLevel= world->getMap()->getWaterLevel(); + for(int j=scaledRect.p[0].y; jgetSurfaceCell(i, j); + SurfaceCell *tc1= map->getSurfaceCell(i, j+1); + + int thisTeamIndex= world->getThisTeamIndex(); + if(tc0->getNearSubmerged() && (tc0->isExplored(thisTeamIndex) || tc1->isExplored(thisTeamIndex))){ + glNormal3f(0.f, 1.f, 0.f); + closed= false; + + triangleCount+= 2; + pointCount+= 2; + + //vertex 1 + glMaterialfv( + GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, + computeWaterColor(waterLevel, tc1->getHeight()).ptr()); + glMultiTexCoord2fv(GL_TEXTURE1, tc1->getFowTexCoord().ptr()); + glTexCoord3f(i, 1.f, waterAnim); + glVertex3f( + static_cast(i)*Map::mapScale, + waterLevel, + static_cast(j+1)*Map::mapScale); + + //vertex 2 + glMaterialfv( + GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, + computeWaterColor(waterLevel, tc0->getHeight()).ptr()); + glMultiTexCoord2fv(GL_TEXTURE1, tc0->getFowTexCoord().ptr()); + glTexCoord3f(i, 0.f, waterAnim); + glVertex3f( + static_cast(i)*Map::mapScale, + waterLevel, + static_cast(j)*Map::mapScale); + + } + else{ + if(!closed){ + + pointCount+= 2; + + //vertex 1 + glMaterialfv( + GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, + computeWaterColor(waterLevel, tc1->getHeight()).ptr()); + glMultiTexCoord2fv(GL_TEXTURE1, tc1->getFowTexCoord().ptr()); + glTexCoord3f(i, 1.f, waterAnim); + glVertex3f( + static_cast(i)*Map::mapScale, + waterLevel, + static_cast(j+1)*Map::mapScale); + + //vertex 2 + glMaterialfv( + GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, + computeWaterColor(waterLevel, tc0->getHeight()).ptr()); + glMultiTexCoord2fv(GL_TEXTURE1, tc0->getFowTexCoord().ptr()); + glTexCoord3f(i, 0.f, waterAnim); + glVertex3f( + static_cast(i)*Map::mapScale, + waterLevel, + static_cast(j)*Map::mapScale); + + glEnd(); + glBegin(GL_TRIANGLE_STRIP); + closed= true; + } + } + } + glEnd(); + } + + //restore + glPopAttrib(); + + assertGl(); +} + +void Renderer::renderUnits(){ + Unit *unit; + const World *world= game->getWorld(); + MeshCallbackTeamColor meshCallbackTeamColor; + + //assert + assertGl(); + + glPushAttrib(GL_ENABLE_BIT | GL_FOG_BIT | GL_LIGHTING_BIT | GL_TEXTURE_BIT); + glEnable(GL_COLOR_MATERIAL); + + if(shadows==sShadowMapping){ + glActiveTexture(shadowTexUnit); + glEnable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, shadowMapHandle); + + static_cast(modelRenderer)->setDuplicateTexCoords(true); + enableProjectiveTexturing(); + } + glActiveTexture(baseTexUnit); + + modelRenderer->begin(true, true, true, &meshCallbackTeamColor); + + for(int i=0; igetFactionCount(); ++i){ + meshCallbackTeamColor.setTeamTexture(world->getFaction(i)->getTexture()); + for(int j=0; jgetFaction(i)->getUnitCount(); ++j){ + unit= world->getFaction(i)->getUnit(j); + if(world->toRenderUnit(unit, visibleQuad)) { + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + + //translate + Vec3f currVec= unit->getCurrVectorFlat(); + glTranslatef(currVec.x, currVec.y, currVec.z); + + //rotate + glRotatef(unit->getRotation(), 0.f, 1.f, 0.f); + glRotatef(unit->getVerticalRotation(), 1.f, 0.f, 0.f); + + //dead alpha + float alpha= 1.0f; + const SkillType *st= unit->getCurrSkill(); + if(st->getClass()==scDie && static_cast(st)->getFade()){ + alpha= 1.0f-unit->getAnimProgress(); + glDisable(GL_COLOR_MATERIAL); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Vec4f(1.0f, 1.0f, 1.0f, alpha).ptr()); + } + else{ + glEnable(GL_COLOR_MATERIAL); + } + + //render + const Model *model= unit->getCurrentModel(); + model->updateInterpolationData(unit->getAnimProgress(), unit->isAlive()); + + //!!! + if(allowRotateUnits == true) { + float rotateAmount = unit->getRotateAmount(); + if(rotateAmount >= 0) { + //if(Socket::enableDebugText) printf("In [%s::%s] rotate unit id = %d amount = %f\n",__FILE__,__FUNCTION__,unit->getId(),rotateAmount); + glRotatef(rotateAmount, 0.f, 1.f, 0.f); + } + } + modelRenderer->render(model); + triangleCount+= model->getTriangleCount(); + pointCount+= model->getVertexCount(); + + glPopMatrix(); + unit->setVisible(true); + } + else + { + unit->setVisible(false); + } + } + } + modelRenderer->end(); + + //restore + static_cast(modelRenderer)->setDuplicateTexCoords(true); + glPopAttrib(); + + //assert + assertGl(); +} + +void Renderer::renderSelectionEffects(){ + + const World *world= game->getWorld(); + const Map *map= world->getMap(); + const Selection *selection= game->getGui()->getSelection(); + + glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT); + glDisable(GL_LIGHTING); + glDisable(GL_TEXTURE_2D); + glDepthFunc(GL_ALWAYS); + glDisable(GL_STENCIL_TEST); + glDisable(GL_CULL_FACE); + glEnable(GL_BLEND); + glLineWidth(2.f); + + //units + for(int i=0; igetCount(); ++i){ + + const Unit *unit= selection->getUnit(i); + + //translate + Vec3f currVec= unit->getCurrVectorFlat(); + currVec.y+= 0.3f; + + //selection circle + if(world->getThisFactionIndex()==unit->getFactionIndex()){ + glColor4f(0, unit->getHpRatio(), 0, 0.3f); + } + else{ + glColor4f(unit->getHpRatio(), 0, 0, 0.3f); + } + renderSelectionCircle(currVec, unit->getType()->getSize(), selectionCircleRadius); + + //magic circle + if(world->getThisFactionIndex()==unit->getFactionIndex() && unit->getType()->getMaxEp()>0){ + glColor4f(unit->getEpRatio()/2.f, unit->getEpRatio(), unit->getEpRatio(), 0.5f); + renderSelectionCircle(currVec, unit->getType()->getSize(), magicCircleRadius); + } + } + + //target arrow + if(selection->getCount()==1){ + const Unit *unit= selection->getUnit(0); + + //comand arrow + if(focusArrows && unit->anyCommand()){ + const CommandType *ct= unit->getCurrCommand()->getCommandType(); + if(ct->getClicks()!=cOne){ + + //arrow color + Vec3f arrowColor; + switch(ct->getClass()){ + case ccMove: + arrowColor= Vec3f(0.f, 1.f, 0.f); + break; + case ccAttack: + case ccAttackStopped: + arrowColor= Vec3f(1.f, 0.f, 0.f); + break; + default: + arrowColor= Vec3f(1.f, 1.f, 0.f); + } + + //arrow target + Vec3f arrowTarget; + Command *c= unit->getCurrCommand(); + if(c->getUnit()!=NULL){ + arrowTarget= c->getUnit()->getCurrVectorFlat(); + } + else{ + Vec2i pos= c->getPos(); + arrowTarget= Vec3f(pos.x, map->getCell(pos)->getHeight(), pos.y); + } + + renderArrow(unit->getCurrVectorFlat(), arrowTarget, arrowColor, 0.3f); + } + } + + //meeting point arrow + if(unit->getType()->getMeetingPoint()){ + Vec2i pos= unit->getMeetingPos(); + Vec3f arrowTarget= Vec3f(pos.x, map->getCell(pos)->getHeight(), pos.y); + renderArrow(unit->getCurrVectorFlat(), arrowTarget, Vec3f(0.f, 0.f, 1.f), 0.3f); + } + + } + + //render selection hightlights + for(int i=0; igetFactionCount(); ++i){ + for(int j=0; jgetFaction(i)->getUnitCount(); ++j){ + const Unit *unit= world->getFaction(i)->getUnit(j); + + if(unit->isHighlighted()){ + float highlight= unit->getHightlight(); + if(game->getWorld()->getThisFactionIndex()==unit->getFactionIndex()){ + glColor4f(0.f, 1.f, 0.f, highlight); + } + else{ + glColor4f(1.f, 0.f, 0.f, highlight); + } + + Vec3f v= unit->getCurrVectorFlat(); + v.y+= 0.3f; + renderSelectionCircle(v, unit->getType()->getSize(), selectionCircleRadius); + } + } + } + + glPopAttrib(); +} + +void Renderer::renderWaterEffects(){ + const World *world= game->getWorld(); + const WaterEffects *we= world->getWaterEffects(); + const Map *map= world->getMap(); + const CoreData &coreData= CoreData::getInstance(); + float height= map->getWaterLevel()+0.001f; + + assertGl(); + + glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT); + glEnable(GL_BLEND); + glDisable(GL_ALPHA_TEST); + glDepthMask(GL_FALSE); + glDepthFunc(GL_LEQUAL); + glEnable(GL_COLOR_MATERIAL); + + glNormal3f(0.f, 1.f, 0.f); + + //splashes + glBindTexture(GL_TEXTURE_2D, static_cast(coreData.getWaterSplashTexture())->getHandle()); + for(int i=0; igetWaterSplashCount(); ++i){ + const WaterSplash *ws= we->getWaterSplash(i); + + //render only if enabled + if(ws->getEnabled()){ + + //render only if visible + Vec2i intPos= Vec2i(static_cast(ws->getPos().x), static_cast(ws->getPos().y)); + if(map->getSurfaceCell(Map::toSurfCoords(intPos))->isVisible(world->getThisTeamIndex())){ + + float scale= ws->getAnim(); + + glColor4f(1.f, 1.f, 1.f, 1.f-ws->getAnim()); + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(0.f, 1.f); + glVertex3f(ws->getPos().x-scale, height, ws->getPos().y+scale); + glTexCoord2f(0.f, 0.f); + glVertex3f(ws->getPos().x-scale, height, ws->getPos().y-scale); + glTexCoord2f(1.f, 1.f); + glVertex3f(ws->getPos().x+scale, height, ws->getPos().y+scale); + glTexCoord2f(1.f, 0.f); + glVertex3f(ws->getPos().x+scale, height, ws->getPos().y-scale); + glEnd(); + } + } + } + + glPopAttrib(); + + assertGl(); +} + +void Renderer::renderMinimap(){ + const World *world= game->getWorld(); + const Minimap *minimap= world->getMinimap(); + const GameCamera *gameCamera= game->getGameCamera(); + const Pixmap2D *pixmap= minimap->getTexture()->getPixmap(); + const Metrics &metrics= Metrics::getInstance(); + + int mx= metrics.getMinimapX(); + int my= metrics.getMinimapY(); + int mw= metrics.getMinimapW(); + int mh= metrics.getMinimapH(); + + Vec2f zoom= Vec2f( + static_cast(mw)/ pixmap->getW(), + static_cast(mh)/ pixmap->getH()); + + assertGl(); + + glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_LINE_BIT | GL_TEXTURE_BIT); + + //draw map + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + + glActiveTexture(fowTexUnit); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, static_cast(minimap->getFowTexture())->getHandle()); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_TEXTURE); + + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_ADD); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_TEXTURE); + + glActiveTexture(baseTexUnit); + glBindTexture(GL_TEXTURE_2D, static_cast(minimap->getTexture())->getHandle()); + + glColor4f(0.5f, 0.5f, 0.5f, 0.1f); + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(0.0f, 1.0f); + glMultiTexCoord2f(fowTexUnit, 0.0f, 1.0f); + glVertex2i(mx, my); + glTexCoord2f(0.0f, 0.0f); + glMultiTexCoord2f(fowTexUnit, 0.0f, 0.0f); + glVertex2i(mx, my+mh); + glTexCoord2f(1.0f, 1.0f); + glMultiTexCoord2f(fowTexUnit, 1.0f, 1.0f); + glVertex2i(mx+mw, my); + glTexCoord2f(1.0f, 0.0f); + glMultiTexCoord2f(fowTexUnit, 1.0f, 0.0f); + glVertex2i(mx+mw, my+mh); + glEnd(); + + glDisable(GL_BLEND); + + glActiveTexture(fowTexUnit); + glDisable(GL_TEXTURE_2D); + glActiveTexture(baseTexUnit); + glDisable(GL_TEXTURE_2D); + + //draw units + glBegin(GL_QUADS); + for(int i=0; igetFactionCount(); ++i){ + for(int j=0; jgetFaction(i)->getUnitCount(); ++j){ + Unit *unit= world->getFaction(i)->getUnit(j); + if(world->toRenderUnit(unit)){ + Vec2i pos= unit->getPos()/Map::cellScale; + int size= unit->getType()->getSize(); + Vec3f color= world->getFaction(i)->getTexture()->getPixmap()->getPixel3f(0, 0); + glColor3fv(color.ptr()); + glVertex2f(mx + pos.x*zoom.x, my + mh - (pos.y*zoom.y)); + glVertex2f(mx + (pos.x+1)*zoom.x+size, my + mh - (pos.y*zoom.y)); + glVertex2f(mx + (pos.x+1)*zoom.x+size, my + mh - ((pos.y+size)*zoom.y)); + glVertex2f(mx + pos.x*zoom.x, my + mh - ((pos.y+size)*zoom.y)); + } + } + } + glEnd(); + + //draw camera + float wRatio= static_cast(metrics.getMinimapW()) / world->getMap()->getW(); + float hRatio= static_cast(metrics.getMinimapH()) / world->getMap()->getH(); + + int x= static_cast(gameCamera->getPos().x * wRatio); + int y= static_cast(gameCamera->getPos().z * hRatio); + + float ang= degToRad(gameCamera->getHAng()); + + glEnable(GL_BLEND); + + glBegin(GL_TRIANGLES); + glColor4f(1.f, 1.f, 1.f, 1.f); + glVertex2i(mx+x, my+mh-y); + + glColor4f(1.f, 1.f, 1.f, 0.0f); + glVertex2i( + mx + x + static_cast(20*sin(ang-pi/5)), + my + mh - (y-static_cast(20*cos(ang-pi/5)))); + + glColor4f(1.f, 1.f, 1.f, 0.0f); + glVertex2i( + mx + x + static_cast(20*sin(ang+pi/5)), + my + mh - (y-static_cast(20*cos(ang+pi/5)))); + + glEnd(); + glPopAttrib(); + + assertGl(); +} + +void Renderer::renderDisplay(){ + + CoreData &coreData= CoreData::getInstance(); + const Metrics &metrics= Metrics::getInstance(); + const Display *display= game->getGui()->getDisplay(); + + glPushAttrib(GL_ENABLE_BIT); + + //infoString + renderTextShadow( + display->getInfoText().c_str(), + coreData.getDisplayFont(), + metrics.getDisplayX(), + metrics.getDisplayY()+Display::infoStringY); + + //title + renderTextShadow( + display->getTitle().c_str(), + coreData.getDisplayFont(), + metrics.getDisplayX()+40, + metrics.getDisplayY() + metrics.getDisplayH() - 20); + + glColor3f(0.0f, 0.0f, 0.0f); + + //text + renderTextShadow( + display->getText().c_str(), + coreData.getDisplayFont(), + metrics.getDisplayX() -1, + metrics.getDisplayY() + metrics.getDisplayH() - 56); + + //progress Bar + if(display->getProgressBar()!=-1){ + renderProgressBar( + display->getProgressBar(), + metrics.getDisplayX(), + metrics.getDisplayY() + metrics.getDisplayH()-50, + coreData.getMenuFontSmall()); + } + + //up images + glEnable(GL_TEXTURE_2D); + + glColor3f(1.f, 1.f, 1.f); + for(int i=0; igetUpImage(i)!=NULL){ + renderQuad( + metrics.getDisplayX()+display->computeUpX(i), + metrics.getDisplayY()+display->computeUpY(i), + Display::imageSize, Display::imageSize, display->getUpImage(i)); + } + } + + //down images + for(int i=0; igetDownImage(i)!=NULL){ + if(display->getDownLighted(i)){ + glColor3f(1.f, 1.f, 1.f); + } + else{ + glColor3f(0.3f, 0.3f, 0.3f); + } + + int x= metrics.getDisplayX()+display->computeDownX(i); + int y= metrics.getDisplayY()+display->computeDownY(i); + int size= Display::imageSize; + + if(display->getDownSelectedPos()==i){ + x-= 3; + y-= 3; + size+= 6; + } + + renderQuad(x, y, size, size, display->getDownImage(i)); + } + } + + //selection + int downPos= display->getDownSelectedPos(); + if(downPos!=Display::invalidPos){ + const Texture2D *texture= display->getDownImage(downPos); + if(texture!=NULL){ + int x= metrics.getDisplayX()+display->computeDownX(downPos)-3; + int y= metrics.getDisplayY()+display->computeDownY(downPos)-3; + int size= Display::imageSize+6; + renderQuad(x, y, size, size, display->getDownImage(downPos)); + } + } + + glPopAttrib(); +} + +void Renderer::renderMenuBackground(const MenuBackground *menuBackground){ + + assertGl(); + + Vec3f cameraPosition= menuBackground->getCamera()->getPosition(); + + glPushAttrib(GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT); + + //clear + Vec4f fogColor= Vec4f(0.4f, 0.4f, 0.4f, 1.f) * menuBackground->getFade(); + glClearColor(fogColor.x, fogColor.y, fogColor.z, 1.f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glFogfv(GL_FOG_COLOR, fogColor.ptr()); + + //light + Vec4f lightPos= Vec4f(10.f, 10.f, 10.f, 1.f)* menuBackground->getFade(); + Vec4f diffLight= Vec4f(0.9f, 0.9f, 0.9f, 1.f)* menuBackground->getFade(); + Vec4f ambLight= Vec4f(0.3f, 0.3f, 0.3f, 1.f)* menuBackground->getFade(); + Vec4f specLight= Vec4f(0.1f, 0.1f, 0.1f, 1.f)* menuBackground->getFade(); + + glEnable(GL_LIGHT0); + glLightfv(GL_LIGHT0, GL_POSITION, lightPos.ptr()); + glLightfv(GL_LIGHT0, GL_DIFFUSE, diffLight.ptr()); + glLightfv(GL_LIGHT0, GL_AMBIENT, ambLight.ptr()); + glLightfv(GL_LIGHT0, GL_SPECULAR, specLight.ptr()); + + //main model + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.5f); + modelRenderer->begin(true, true, true); + modelRenderer->render(menuBackground->getMainModel()); + modelRenderer->end(); + glDisable(GL_ALPHA_TEST); + + //characters + float dist= menuBackground->getAboutPosition().dist(cameraPosition); + float minDist= 3.f; + if(distbegin(true, true, false); + + for(int i=0; igetCharacterModel(i)->updateInterpolationData(menuBackground->getAnim(), true); + modelRenderer->render(menuBackground->getCharacterModel(i)); + glPopMatrix(); + } + modelRenderer->end(); + } + + //water + if(menuBackground->getWater()){ + + //water surface + const int waterTesselation= 10; + const int waterSize= 250; + const int waterQuadSize= 2*waterSize/waterTesselation; + const float waterHeight= menuBackground->getWaterHeight(); + + glEnable(GL_BLEND); + + glNormal3f(0.f, 1.f, 0.f); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Vec4f(1.f, 1.f, 1.f, 1.f).ptr()); + GLuint waterHandle= static_cast(menuBackground->getWaterTexture())->getHandle(); + glBindTexture(GL_TEXTURE_2D, waterHandle); + for(int i=1; igetRain()){ + const float maxRaindropAlpha= 0.5f; + + glEnable(GL_BLEND); + glDisable(GL_LIGHTING); + glDisable(GL_ALPHA_TEST); + glDepthMask(GL_FALSE); + + //splashes + CoreData &coreData= CoreData::getInstance(); + glBindTexture(GL_TEXTURE_2D, static_cast(coreData.getWaterSplashTexture())->getHandle()); + for(int i=0; igetRaindropPos(i); + float scale= menuBackground->getRaindropState(i); + float alpha= maxRaindropAlpha-scale*maxRaindropAlpha; + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + + glColor4f(1.f, 1.f, 1.f, alpha); + glTranslatef(pos.x, waterHeight+0.01f, pos.y); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(0.f, 1.f); + glVertex3f(-scale, 0, scale); + glTexCoord2f(0.f, 0.f); + glVertex3f(-scale, 0, -scale); + glTexCoord2f(1.f, 1.f); + glVertex3f(scale, 0, scale); + glTexCoord2f(1.f, 0.f); + glVertex3f(scale, 0, -scale); + glEnd(); + + glPopMatrix(); + } + } + } + + glPopAttrib(); + + assertGl(); +} + +// ==================== computing ==================== + +bool Renderer::computePosition(const Vec2i &screenPos, Vec2i &worldPos){ + + assertGl(); + const Map* map= game->getWorld()->getMap(); + const Metrics &metrics= Metrics::getInstance(); + float depth= 0.0f; + GLdouble modelviewMatrix[16]; + GLdouble projectionMatrix[16]; + GLint viewport[4]= {0, 0, metrics.getScreenW(), metrics.getScreenH()}; + GLdouble worldX; + GLdouble worldY; + GLdouble worldZ; + GLint screenX= (screenPos.x * metrics.getScreenW() / metrics.getVirtualW()); + GLint screenY= (screenPos.y * metrics.getScreenH() / metrics.getVirtualH()); + + //get the depth in the cursor pixel + glReadPixels(screenX, screenY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth); + + //load matrices + loadProjectionMatrix(); + loadGameCameraMatrix(); + + //get matrices + glGetDoublev(GL_MODELVIEW_MATRIX, modelviewMatrix); + glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix); + + //get the world coordinates + gluUnProject( + screenX, screenY, depth, + modelviewMatrix, projectionMatrix, viewport, + &worldX, &worldY, &worldZ); + + //conver coords to int + worldPos= Vec2i(static_cast(worldX+0.5f), static_cast(worldZ+0.5f)); + + //clamp coords to map size + return map->isInside(worldPos); +} + +void Renderer::computeSelected(Selection::UnitContainer &units, const Vec2i &posDown, const Vec2i &posUp){ + + //declarations + GLuint selectBuffer[Gui::maxSelBuff]; + const Metrics &metrics= Metrics::getInstance(); + + //compute center and dimensions of selection rectangle + int x= (posDown.x+posUp.x) / 2; + int y= (posDown.y+posUp.y) / 2; + int w= abs(posDown.x-posUp.x); + int h= abs(posDown.y-posUp.y); + if(w<1) w=1; + if(h<1) h=1; + + //setup matrices + glSelectBuffer(Gui::maxSelBuff, selectBuffer); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + GLint view[]= {0, 0, metrics.getVirtualW(), metrics.getVirtualH()}; + glRenderMode(GL_SELECT); + glLoadIdentity(); + gluPickMatrix(x, y, w, h, view); + gluPerspective(perspFov, metrics.getAspectRatio(), perspNearPlane, perspFarPlane); + loadGameCameraMatrix(); + + //render units + renderUnitsFast(); + + //pop matrices + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + //select units + int selCount= glRenderMode(GL_RENDER); + for(int i=1; i<=selCount; ++i){ + int factionIndex= selectBuffer[i*5-2]; + int unitIndex= selectBuffer[i*5-1]; + const World *world= game->getWorld(); + if(factionIndexgetFactionCount() && unitIndexgetFaction(factionIndex)->getUnitCount()){ + Unit *unit= world->getFaction(factionIndex)->getUnit(unitIndex); + if(unit->isAlive()){ + units.push_back(unit); + } + } + } +} + + +// ==================== shadows ==================== + +void Renderer::renderShadowsToTexture(){ + + if(shadows==sProjected || shadows==sShadowMapping){ + + shadowMapFrame= (shadowMapFrame + 1) % (shadowFrameSkip + 1); + + if(shadowMapFrame==0){ + + assertGl(); + + glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT | GL_POLYGON_BIT); + + if(shadows==sShadowMapping){ + glClear(GL_DEPTH_BUFFER_BIT); + } + else{ + float color= 1.0f-shadowAlpha; + glColor3f(color, color, color); + glClearColor(1.f, 1.f, 1.f, 1.f); + glDisable(GL_DEPTH_TEST); + glClear(GL_COLOR_BUFFER_BIT); + } + + //clear color buffer + // + //set viewport, we leave one texel always in white to avoid problems + glViewport(1, 1, shadowTextureSize-2, shadowTextureSize-2); + + if(nearestLightPos.w==0.f){ + //directional light + + //light pos + const TimeFlow *tf= game->getWorld()->getTimeFlow(); + float ang= tf->isDay()? computeSunAngle(tf->getTime()): computeMoonAngle(tf->getTime()); + ang= radToDeg(ang); + + //push and set projection + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + if(game->getGameCamera()->getState()==GameCamera::sGame){ + glOrtho(-35, 5, -15, 15, -1000, 1000); + } + else{ + glOrtho(-30, 30, -20, 20, -1000, 1000); + } + + //push and set modelview + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glRotatef(15, 0, 1, 0); + + glRotatef(ang, 1, 0, 0); + glRotatef(90, 0, 1, 0); + Vec3f pos= game->getGameCamera()->getPos(); + + glTranslatef(static_cast(-pos.x), 0, static_cast(-pos.z)); + + } + else{ + //non directional light + + //push projection + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + gluPerspective(150, 1.f, perspNearPlane, perspFarPlane); + + //push modelview + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glRotatef(-90, -1, 0, 0); + glTranslatef(-nearestLightPos.x, -nearestLightPos.y-2, -nearestLightPos.z); + } + + if(shadows==sShadowMapping){ + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(1.0f, 0.001f); + } + + //render 3d + renderUnitsFast(); + renderObjectsFast(); + + //read color buffer + glBindTexture(GL_TEXTURE_2D, shadowMapHandle); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, shadowTextureSize, shadowTextureSize); + + //get elemental matrices + Matrix4f matrix1; + matrix1[0]= 0.5f; matrix1[4]= 0.f; matrix1[8]= 0.f; matrix1[12]= 0.5f; + matrix1[1]= 0.f; matrix1[5]= 0.5f; matrix1[9]= 0.f; matrix1[13]= 0.5f; + matrix1[2]= 0.f; matrix1[6]= 0.f; matrix1[10]= 0.5f; matrix1[14]= 0.5f; + matrix1[3]= 0.f; matrix1[7]= 0.f; matrix1[11]= 0.f; matrix1[15]= 1.f; + + Matrix4f matrix2; + glGetFloatv(GL_PROJECTION_MATRIX, matrix2.ptr()); + + Matrix4f matrix3; + glGetFloatv(GL_MODELVIEW_MATRIX, matrix3.ptr()); + + //pop both matrices + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + + //compute texture matrix + glLoadMatrixf(matrix1.ptr()); + glMultMatrixf(matrix2.ptr()); + glMultMatrixf(matrix3.ptr()); + glGetFloatv(GL_TRANSPOSE_PROJECTION_MATRIX_ARB, shadowMapMatrix.ptr()); + + //pop + glPopMatrix(); + + glPopAttrib(); + + assertGl(); + } + } +} + + +// ==================== gl wrap ==================== + +string Renderer::getGlInfo(){ + string infoStr; + Lang &lang= Lang::getInstance(); + + infoStr+= lang.get("OpenGlInfo")+":\n"; + infoStr+= " "+lang.get("OpenGlVersion")+": "; + infoStr+= string(getGlVersion())+"\n"; + infoStr+= " "+lang.get("OpenGlRenderer")+": "; + infoStr+= string(getGlRenderer())+"\n"; + infoStr+= " "+lang.get("OpenGlVendor")+": "; + infoStr+= string(getGlVendor())+"\n"; + infoStr+= " "+lang.get("OpenGlMaxLights")+": "; + infoStr+= intToStr(getGlMaxLights())+"\n"; + infoStr+= " "+lang.get("OpenGlMaxTextureSize")+": "; + infoStr+= intToStr(getGlMaxTextureSize())+"\n"; + infoStr+= " "+lang.get("OpenGlMaxTextureUnits")+": "; + infoStr+= intToStr(getGlMaxTextureUnits())+"\n"; + infoStr+= " "+lang.get("OpenGlModelviewStack")+": "; + infoStr+= intToStr(getGlModelviewMatrixStackDepth())+"\n"; + infoStr+= " "+lang.get("OpenGlProjectionStack")+": "; + infoStr+= intToStr(getGlProjectionMatrixStackDepth())+"\n"; + + return infoStr; +} + +string Renderer::getGlMoreInfo(){ + string infoStr; + Lang &lang= Lang::getInstance(); + + //gl extensions + infoStr+= lang.get("OpenGlExtensions")+":\n "; + + string extensions= getGlExtensions(); + int charCount= 0; + for(int i=0; i120 && extensions[i]==' '){ + infoStr+= "\n "; + charCount= 0; + } + ++charCount; + } + + //platform extensions + infoStr+= "\n\n"; + infoStr+= lang.get("OpenGlPlatformExtensions")+":\n "; + + charCount= 0; + string platformExtensions= getGlPlatformExtensions(); + for(int i=0; i120 && platformExtensions[i]==' '){ + infoStr+= "\n "; + charCount= 0; + } + ++charCount; + } + + return infoStr; +} + +void Renderer::autoConfig(){ + + Config &config= Config::getInstance(); + bool nvidiaCard= toLower(getGlVendor()).find("nvidia")!=string::npos; + bool atiCard= toLower(getGlVendor()).find("ati")!=string::npos; + bool shadowExtensions = isGlExtensionSupported("GL_ARB_shadow") && isGlExtensionSupported("GL_ARB_shadow_ambient"); + + //3D textures + config.setBool("Textures3D", isGlExtensionSupported("GL_EXT_texture3D")); + + //shadows + string shadows; + if(getGlMaxTextureUnits()>=3){ + if(nvidiaCard && shadowExtensions){ + shadows= shadowsToStr(sShadowMapping); + } + else{ + shadows= shadowsToStr(sProjected); + } + } + else{ + shadows=shadowsToStr(sDisabled); + } + config.setString("Shadows", shadows); + + //lights + config.setInt("MaxLights", atiCard? 1: 4); + + //filter + config.setString("Filter", "Bilinear"); +} + +void Renderer::clearBuffers(){ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void Renderer::clearZBuffer(){ + glClear(GL_DEPTH_BUFFER_BIT); +} + +void Renderer::loadConfig(){ + Config &config= Config::getInstance(); + + //cache most used config params + maxLights= config.getInt("MaxLights"); + photoMode= config.getBool("PhotoMode"); + focusArrows= config.getBool("FocusArrows"); + textures3D= config.getBool("Textures3D"); + + //load shadows + shadows= strToShadows(config.getString("Shadows")); + if(shadows==sProjected || shadows==sShadowMapping){ + shadowTextureSize= config.getInt("ShadowTextureSize"); + shadowFrameSkip= config.getInt("ShadowFrameSkip"); + shadowAlpha= config.getFloat("ShadowAlpha"); + } + + //load filter settings + Texture2D::Filter textureFilter= strToTextureFilter(config.getString("Filter")); + int maxAnisotropy= config.getInt("FilterMaxAnisotropy"); + for(int i=0; isetFilter(textureFilter); + textureManager[i]->setMaxAnisotropy(maxAnisotropy); + } +} + +void Renderer::saveScreen(const string &path){ + + const Metrics &sm= Metrics::getInstance(); + + Pixmap2D pixmap(sm.getScreenW(), sm.getScreenH(), 3); + + glReadPixels(0, 0, pixmap.getW(), pixmap.getH(), GL_RGB, GL_UNSIGNED_BYTE, pixmap.getPixels()); + pixmap.saveTga(path); +} + +// ==================== PRIVATE ==================== + +float Renderer::computeSunAngle(float time){ + + float dayTime= TimeFlow::dusk-TimeFlow::dawn; + float fTime= (time-TimeFlow::dawn)/dayTime; + return clamp(fTime*pi, pi/8.f, 7.f*pi/8.f); +} + +float Renderer::computeMoonAngle(float time){ + float nightTime= 24-(TimeFlow::dusk-TimeFlow::dawn); + + if(timegetWorld()->getTileset(); + Vec3f color; + + const float transition= 2; + const float dayStart= TimeFlow::dawn; + const float dayEnd= TimeFlow::dusk-transition; + const float nightStart= TimeFlow::dusk; + const float nightEnd= TimeFlow::dawn-transition; + + if(time>dayStart && timegetSunLightColor(); + } + else if(time>nightStart || timegetMoonLightColor(); + } + else if(time>=dayEnd && time<=nightStart){ + color= tileset->getSunLightColor().lerp((time-dayEnd)/transition, tileset->getMoonLightColor()); + } + else if(time>=nightEnd && time<=dayStart){ + color= tileset->getMoonLightColor().lerp((time-nightEnd)/transition, tileset->getSunLightColor()); + } + else{ + assert(false); + color= tileset->getSunLightColor(); + } + return color; +} + +Vec4f Renderer::computeWaterColor(float waterLevel, float cellHeight){ + const float waterFactor= 1.5f; + return Vec4f(1.f, 1.f, 1.f, clamp((waterLevel-cellHeight)*waterFactor, 0.f, 1.f)); +} + +// ==================== fast render ==================== + +//render units for selection purposes +void Renderer::renderUnitsFast(){ + const World *world= game->getWorld(); + + assertGl(); + + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); + + modelRenderer->begin(false, false, false); + glInitNames(); + for(int i=0; igetFactionCount(); ++i){ + glPushName(i); + for(int j=0; jgetFaction(i)->getUnitCount(); ++j){ + glPushName(j); + Unit *unit= world->getFaction(i)->getUnit(j); + if(world->toRenderUnit(unit, visibleQuad)) { + glMatrixMode(GL_MODELVIEW); + + //debuxar modelo + glPushMatrix(); + + //translate + Vec3f currVec= unit->getCurrVectorFlat(); + glTranslatef(currVec.x, currVec.y, currVec.z); + + //rotate + glRotatef(unit->getRotation(), 0.f, 1.f, 0.f); + + //render + const Model *model= unit->getCurrentModel(); + model->updateInterpolationVertices(unit->getAnimProgress(), unit->isAlive()); + + //!!! + if(allowRotateUnits == true) { + float rotateAmount = unit->getRotateAmount(); + if(rotateAmount >= 0) { + //if(Socket::enableDebugText) printf("In [%s::%s] rotate unit id = %d amount = %f\n",__FILE__,__FUNCTION__,unit->getId(),rotateAmount); + glRotatef(rotateAmount, 0.f, 1.f, 0.f); + } + } + modelRenderer->render(model); + + glPopMatrix(); + + } + glPopName(); + } + glPopName(); + } + modelRenderer->end(); + + glPopAttrib(); +} + +//render objects for selection purposes +void Renderer::renderObjectsFast(){ + const World *world= game->getWorld(); + const Map *map= world->getMap(); + + assertGl(); + + glPushAttrib(GL_ENABLE_BIT| GL_TEXTURE_BIT); + glDisable(GL_LIGHTING); + + glAlphaFunc(GL_GREATER, 0.5f); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + + //set color to the texture alpha + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + + //set alpha to the texture alpha + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + + modelRenderer->begin(false, true, false); + int thisTeamIndex= world->getThisTeamIndex(); + + PosQuadIterator pqi(map, visibleQuad, Map::cellScale); + while(pqi.next()){ + const Vec2i pos= pqi.getPos(); + + if(map->isInside(pos)){ + + SurfaceCell *sc= map->getSurfaceCell(Map::toSurfCoords(pos)); + Object *o= sc->getObject(); + if(sc->isExplored(thisTeamIndex) && o!=NULL){ + + const Model *objModel= sc->getObject()->getModel(); + Vec3f v= o->getPos(); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glTranslatef(v.x, v.y, v.z); + glRotatef(o->getRotation(), 0.f, 1.f, 0.f); + + modelRenderer->render(objModel); + + glPopMatrix(); + + } + } + } + + modelRenderer->end(); + + glPopAttrib(); + + assertGl(); +} + +// ==================== gl caps ==================== + +void Renderer::checkGlCaps(){ + + //opengl 1.3 + if(!isGlVersionSupported(1, 3, 0)){ + string message; + + message += "Your system supports OpenGL version \""; + message += getGlVersion() + string("\"\n"); + message += "Glest needs at least version 1.3 to work\n"; + message += "You may solve this problem by installing your latest video card drivers"; + + throw runtime_error(message.c_str()); + } + + //opengl 1.4 or extension + if(!isGlVersionSupported(1, 4, 0)){ + checkExtension("GL_ARB_texture_env_crossbar", "Glest"); + } +} + +void Renderer::checkGlOptionalCaps(){ + + //shadows + if(shadows==sProjected || shadows==sShadowMapping){ + if(getGlMaxTextureUnits()<3){ + throw runtime_error("Your system doesn't support 3 texture units, required for shadows"); + } + } + + //shadow mapping + if(shadows==sShadowMapping){ + checkExtension("GL_ARB_shadow", "Shadow Mapping"); + checkExtension("GL_ARB_shadow_ambient", "Shadow Mapping"); + } +} + +void Renderer::checkExtension(const string &extension, const string &msg){ + if(!isGlExtensionSupported(extension.c_str())){ + string str= "OpenGL extension not supported: " + extension + ", required for " + msg; + throw runtime_error(str); + } +} + +// ==================== init 3d lists ==================== + +void Renderer::init3dList(){ + + const Metrics &metrics= Metrics::getInstance(); + + assertGl(); + + list3d= glGenLists(1); + glNewList(list3d, GL_COMPILE_AND_EXECUTE); + //need to execute, because if not gluPerspective takes no effect and gluLoadMatrix is wrong + + //misc + glViewport(0, 0, metrics.getScreenW(), metrics.getScreenH()); + glClearColor(fowColor.x, fowColor.y, fowColor.z, fowColor.w); + glFrontFace(GL_CW); + glEnable(GL_CULL_FACE); + loadProjectionMatrix(); + + //texture state + glActiveTexture(shadowTexUnit); + glDisable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glActiveTexture(fowTexUnit); + glDisable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glActiveTexture(baseTexUnit); + glEnable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + //material state + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, defSpecularColor.ptr()); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, defAmbientColor.ptr()); + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, defDiffuseColor.ptr()); + glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); + glColor4fv(defColor.ptr()); + + //blend state + glDisable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + //alpha test state + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.f); + + //depth test state + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glDepthFunc(GL_LESS); + + //lighting state + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + + //matrix mode + glMatrixMode(GL_MODELVIEW); + + //stencil test + glDisable(GL_STENCIL_TEST); + + //fog + const Tileset *tileset= game->getWorld()->getTileset(); + if(tileset->getFog()){ + glEnable(GL_FOG); + if(tileset->getFogMode()==fmExp){ + glFogi(GL_FOG_MODE, GL_EXP); + } + else{ + glFogi(GL_FOG_MODE, GL_EXP2); + } + + glFogf(GL_FOG_DENSITY, tileset->getFogDensity()); + glFogfv(GL_FOG_COLOR, tileset->getFogColor().ptr()); + } + + glEndList(); + + //assert + assertGl(); + +} + +void Renderer::init2dList(){ + + const Metrics &metrics= Metrics::getInstance(); + + //this list sets the state for the 2d rendering + list2d= glGenLists(1); + glNewList(list2d, GL_COMPILE); + + //projection + glViewport(0, 0, metrics.getScreenW(), metrics.getScreenH()); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, metrics.getVirtualW(), 0, metrics.getVirtualH(), 0, 1); + + //modelview + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + //disable everything + glDisable(GL_BLEND); + glDisable(GL_LIGHTING); + glDisable(GL_ALPHA_TEST); + glDisable(GL_DEPTH_TEST); + glDisable(GL_STENCIL_TEST); + glDisable(GL_FOG); + glDisable(GL_CULL_FACE); + glFrontFace(GL_CCW); + glActiveTexture(baseTexUnit); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glDisable(GL_TEXTURE_2D); + + //blend func + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + //color + glColor4f(1.f, 1.f, 1.f, 1.f); + + glEndList(); + + assertGl(); +} + +void Renderer::init3dListMenu(MainMenu *mm){ + assertGl(); + + const Metrics &metrics= Metrics::getInstance(); + const MenuBackground *mb= mm->getMenuBackground(); + + list3dMenu= glGenLists(1); + glNewList(list3dMenu, GL_COMPILE); + + //misc + glViewport(0, 0, metrics.getScreenW(), metrics.getScreenH()); + glClearColor(0.4f, 0.4f, 0.4f, 1.f); + glFrontFace(GL_CW); + glEnable(GL_CULL_FACE); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(perspFov, metrics.getAspectRatio(), perspNearPlane, 1000); + + //texture state + glEnable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + //material state + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, defSpecularColor.ptr()); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, defAmbientColor.ptr()); + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, defDiffuseColor.ptr()); + glColor4fv(defColor.ptr()); + glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); + + //blend state + glDisable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + //alpha test state + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.f); + + //depth test state + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glDepthFunc(GL_LESS); + + //lighting state + glEnable(GL_LIGHTING); + + //matrix mode + glMatrixMode(GL_MODELVIEW); + + //stencil test + glDisable(GL_STENCIL_TEST); + + //fog + if(mb->getFog()){ + glEnable(GL_FOG); + glFogi(GL_FOG_MODE, GL_EXP2); + glFogf(GL_FOG_DENSITY, mb->getFogDensity()); + } + + glEndList(); + + //assert + assertGl(); +} + + +// ==================== misc ==================== + +void Renderer::loadProjectionMatrix(){ + GLdouble clipping; + const Metrics &metrics= Metrics::getInstance(); + + assertGl(); + + clipping= photoMode ? perspFarPlane*100 : perspFarPlane; + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(perspFov, metrics.getAspectRatio(), perspNearPlane, clipping); + + assertGl(); +} + +void Renderer::enableProjectiveTexturing(){ + glTexGenfv(GL_S, GL_EYE_PLANE, &shadowMapMatrix[0]); + glTexGenfv(GL_T, GL_EYE_PLANE, &shadowMapMatrix[4]); + glTexGenfv(GL_R, GL_EYE_PLANE, &shadowMapMatrix[8]); + glTexGenfv(GL_Q, GL_EYE_PLANE, &shadowMapMatrix[12]); + glEnable(GL_TEXTURE_GEN_S); + glEnable(GL_TEXTURE_GEN_T); + glEnable(GL_TEXTURE_GEN_R); + glEnable(GL_TEXTURE_GEN_Q); +} + +// ==================== private aux drawing ==================== + +void Renderer::renderSelectionCircle(Vec3f v, int size, float radius){ + GLUquadricObj *disc; + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + + glTranslatef(v.x, v.y, v.z); + glRotatef(90.f, 1.f, 0.f, 0.f); + disc= gluNewQuadric(); + gluQuadricDrawStyle(disc, GLU_FILL); + gluCylinder(disc, radius*(size-0.2f), radius*size, 0.2f, 30, 1); + gluDeleteQuadric(disc); + + glPopMatrix(); +} + +void Renderer::renderArrow(const Vec3f &pos1, const Vec3f &pos2, const Vec3f &color, float width){ + const int tesselation= 3; + const float arrowEndSize= 0.4f; + const float maxlen= 25; + const float blendDelay= 5.f; + + Vec3f dir= Vec3f(pos2-pos1); + float len= dir.length(); + + if(len>maxlen){ + return; + } + float alphaFactor= clamp((maxlen-len)/blendDelay, 0.f, 1.f); + + dir.normalize(); + Vec3f normal= dir.cross(Vec3f(0, 1, 0)); + + Vec3f pos2Left= pos2 + normal*(width-0.05f) - dir*arrowEndSize*width; + Vec3f pos2Right= pos2 - normal*(width-0.05f) - dir*arrowEndSize*width; + Vec3f pos1Left= pos1 + normal*(width+0.05f); + Vec3f pos1Right= pos1 - normal*(width+0.05f); + + //arrow body + glBegin(GL_TRIANGLE_STRIP); + for(int i=0; i<=tesselation; ++i){ + float t= static_cast(i)/tesselation; + Vec3f a= pos1Left.lerp(t, pos2Left); + Vec3f b= pos1Right.lerp(t, pos2Right); + Vec4f c= Vec4f(color, t*0.25f*alphaFactor); + + glColor4fv(c.ptr()); + glVertex3fv(a.ptr()); + glVertex3fv(b.ptr()); + } + glEnd(); + + //arrow end + glBegin(GL_TRIANGLES); + glVertex3fv((pos2Left + normal*(arrowEndSize-0.1f)).ptr()); + glVertex3fv((pos2Right - normal*(arrowEndSize-0.1f)).ptr()); + glVertex3fv((pos2 + dir*(arrowEndSize-0.1f)).ptr()); + glEnd(); +} + +void Renderer::renderProgressBar(int size, int x, int y, Font2D *font){ + + //bar + glBegin(GL_QUADS); + glColor4fv(progressBarFront2.ptr()); + glVertex2i(x, y); + glVertex2i(x, y+10); + glColor4fv(progressBarFront1.ptr()); + glVertex2i(x+size, y+10); + glVertex2i(x+size, y); + glEnd(); + + //transp bar + glEnable(GL_BLEND); + glBegin(GL_QUADS); + glColor4fv(progressBarBack2.ptr()); + glVertex2i(x+size, y); + glVertex2i(x+size, y+10); + glColor4fv(progressBarBack1.ptr()); + glVertex2i(x+maxProgressBar, y+10); + glVertex2i(x+maxProgressBar, y); + glEnd(); + glDisable(GL_BLEND); + + //text + glColor3fv(defColor.ptr()); + textRenderer->begin(font); + textRenderer->render(intToStr(static_cast(size))+"%", x+maxProgressBar/2, y, true); + textRenderer->end(); +} + + +void Renderer::renderTile(const Vec2i &pos){ + + const Map *map= game->getWorld()->getMap(); + Vec2i scaledPos= pos * Map::cellScale; + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glTranslatef(-0.5f, 0.f, -0.5f); + + glInitNames(); + for(int i=0; i(renderPos.x), + map->getCell(renderPos.x, renderPos.y)->getHeight(), + static_cast(renderPos.y)); + glVertex3f( + static_cast(renderPos.x), + map->getCell(renderPos.x, renderPos.y+1)->getHeight(), + static_cast(renderPos.y+1)); + glVertex3f( + static_cast(renderPos.x+1), + map->getCell(renderPos.x+1, renderPos.y)->getHeight(), + static_cast(renderPos.y)); + glVertex3f( + static_cast(renderPos.x+1), + map->getCell(renderPos.x+1, renderPos.y+1)->getHeight(), + static_cast(renderPos.y+1)); + glEnd(); + + glPopName(); + glPopName(); + } + } + + glPopMatrix(); +} + +void Renderer::renderQuad(int x, int y, int w, int h, const Texture2D *texture){ + glBindTexture(GL_TEXTURE_2D, static_cast(texture)->getHandle()); + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2i(0, 1); + glVertex2i(x, y+h); + glTexCoord2i(0, 0); + glVertex2i(x, y); + glTexCoord2i(1, 1); + glVertex2i(x+w, y+h); + glTexCoord2i(1, 0); + glVertex2i(x+w, y); + glEnd(); +} + +Renderer::Shadows Renderer::strToShadows(const string &s){ + if(s=="Projected"){ + return sProjected; + } + else if(s=="ShadowMapping"){ + return sShadowMapping; + } + return sDisabled; +} + +string Renderer::shadowsToStr(Shadows shadows){ + switch(shadows){ + case sDisabled: + return "Disabled"; + case sProjected: + return "Projected"; + case sShadowMapping: + return "ShadowMapping"; + default: + assert(false); + return ""; + } +} + +Texture2D::Filter Renderer::strToTextureFilter(const string &s){ + if(s=="Bilinear"){ + return Texture2D::fBilinear; + } + else if(s=="Trilinear"){ + return Texture2D::fTrilinear; + } + + throw runtime_error("Error converting from string to FilterType, found: "+s); +} + +}}//end namespace diff --git a/source/glest_game/graphics/renderer.h b/source/glest_game/graphics/renderer.h new file mode 100644 index 00000000..03c54d69 --- /dev/null +++ b/source/glest_game/graphics/renderer.h @@ -0,0 +1,309 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_RENDERER_H_ +#define _GLEST_GAME_RENDERER_H_ + +#include "vec.h" +#include "math_util.h" +#include "model.h" +#include "particle.h" +#include "pixmap.h" +#include "font.h" +#include "matrix.h" +#include "selection.h" +#include "components.h" +#include "texture.h" +#include "model_manager.h" +#include "graphics_factory_gl.h" +#include "font_manager.h" +#include "camera.h" + +namespace Glest{ namespace Game{ + +using Shared::Graphics::Texture2D; +using Shared::Graphics::Texture3D; +using Shared::Graphics::ModelRenderer; +using Shared::Graphics::TextRenderer2D; +using Shared::Graphics::ParticleRenderer; +using Shared::Graphics::ParticleManager; +using Shared::Graphics::ModelManager; +using Shared::Graphics::TextureManager; +using Shared::Graphics::FontManager; +using Shared::Graphics::Font2D; +using Shared::Graphics::Matrix4f; +using Shared::Graphics::Vec2i; +using Shared::Graphics::Quad2i; +using Shared::Graphics::Vec3f; +using Shared::Graphics::Model; +using Shared::Graphics::ParticleSystem; +using Shared::Graphics::Pixmap2D; +using Shared::Graphics::Camera; + +//non shared classes +class Config; +class Game; +class MainMenu; +class Console; +class MenuBackground; +class ChatManager; + +enum ResourceScope{ + rsGlobal, + rsMenu, + rsGame, + + rsCount +}; + +// =========================================================== +// class Renderer +// +/// OpenGL renderer, uses the shared library +// =========================================================== + +class Renderer{ +public: + //progress bar + static const int maxProgressBar; + static const Vec4f progressBarBack1; + static const Vec4f progressBarBack2; + static const Vec4f progressBarFront1; + static const Vec4f progressBarFront2; + + //sun and moon + static const float sunDist; + static const float moonDist; + static const float lightAmbFactor; + + //mouse + static const int maxMouse2dAnim; + + //texture units + static const GLenum baseTexUnit; + static const GLenum fowTexUnit; + static const GLenum shadowTexUnit; + + //selection + static const float selectionCircleRadius; + static const float magicCircleRadius; + + //perspective values + static const float perspFov; + static const float perspNearPlane; + static const float perspFarPlane; + + //default values + static const float ambFactor; + static const Vec4f defSpecularColor; + static const Vec4f defDiffuseColor; + static const Vec4f defAmbientColor; + static const Vec4f defColor; + static const Vec4f fowColor; + + //light + static const float maxLightDist; + +public: + enum Shadows{ + sDisabled, + sProjected, + sShadowMapping, + + sCount + }; + +private: + //config + int maxLights; + bool photoMode; + int shadowTextureSize; + int shadowFrameSkip; + float shadowAlpha; + bool focusArrows; + bool textures3D; + Shadows shadows; + + //game + const Game *game; + + //misc + int triangleCount; + int pointCount; + Quad2i visibleQuad; + Vec4f nearestLightPos; + + //renderers + ModelRenderer *modelRenderer; + TextRenderer2D *textRenderer; + ParticleRenderer *particleRenderer; + + //texture managers + ModelManager *modelManager[rsCount]; + TextureManager *textureManager[rsCount]; + FontManager *fontManager[rsCount]; + ParticleManager *particleManager[rsCount]; + + //state lists + GLuint list3d; + GLuint list2d; + GLuint list3dMenu; + + //shadows + GLuint shadowMapHandle; + Matrix4f shadowMapMatrix; + int shadowMapFrame; + + //water + float waterAnim; + + bool allowRotateUnits; + +private: + Renderer(); + ~Renderer(); + +public: + static Renderer &getInstance(); + + //init + void init(); + void initGame(Game *game); + void initMenu(MainMenu *mm); + void reset3d(); + void reset2d(); + void reset3dMenu(); + + //end + void end(); + void endMenu(); + void endGame(); + + //get + int getTriangleCount() const {return triangleCount;} + int getPointCount() const {return pointCount;} + + //misc + void reloadResources(); + + //engine interface + Model *newModel(ResourceScope rs); + Texture2D *newTexture2D(ResourceScope rs); + Texture3D *newTexture3D(ResourceScope rs); + Font2D *newFont(ResourceScope rs); + TextRenderer2D *getTextRenderer() const {return textRenderer;} + void manageParticleSystem(ParticleSystem *particleSystem, ResourceScope rs); + void updateParticleManager(ResourceScope rs); + void renderParticleManager(ResourceScope rs); + void swapBuffers(); + + //lights and camera + void setupLighting(); + void loadGameCameraMatrix(); + void loadCameraMatrix(const Camera *camera); + void computeVisibleQuad(); + + //basic rendering + void renderMouse2d(int mouseX, int mouseY, int anim, float fade= 0.f); + void renderMouse3d(); + void renderBackground(const Texture2D *texture); + void renderTextureQuad(int x, int y, int w, int h, const Texture2D *texture, float alpha=1.f); + void renderConsole(const Console *console); + void renderChatManager(const ChatManager *chatManager); + void renderResourceStatus(); + void renderSelectionQuad(); + void renderText(const string &text, const Font2D *font, float alpha, int x, int y, bool centered= false); + void renderText(const string &text, const Font2D *font, const Vec3f &color, int x, int y, bool centered= false); + void renderTextShadow(const string &text, const Font2D *font, int x, int y, bool centered= false); + + //components + void renderLabel(const GraphicLabel *label); + void renderButton(const GraphicButton *button); + void renderListBox(const GraphicListBox *listBox); + void renderMessageBox(const GraphicMessageBox *listBox); + + //complex rendering + void renderSurface(); + void renderObjects(); + void renderWater(); + void renderUnits(); + void renderSelectionEffects(); + void renderWaterEffects(); + void renderMinimap(); + void renderDisplay(); + void renderMenuBackground(const MenuBackground *menuBackground); + + //computing + bool computePosition(const Vec2i &screenPos, Vec2i &worldPos); + void computeSelected(Selection::UnitContainer &units, const Vec2i &posDown, const Vec2i &posUp); + + //gl wrap + string getGlInfo(); + string getGlMoreInfo(); + void autoConfig(); + + //clear + void clearBuffers(); + void clearZBuffer(); + + //shadows + void renderShadowsToTexture(); + + //misc + void loadConfig(); + void saveScreen(const string &path); + Quad2i getVisibleQuad() const {return visibleQuad;} + + //static + static Shadows strToShadows(const string &s); + static string shadowsToStr(Shadows shadows); + +private: + //private misc + float computeSunAngle(float time); + float computeMoonAngle(float time); + Vec4f computeSunPos(float time); + Vec4f computeMoonPos(float time); + Vec3f computeLightColor(float time); + Vec4f computeWaterColor(float waterLevel, float cellHeight); + void checkExtension(const string &extension, const string &msg); + + //selection render + void renderObjectsFast(); + void renderUnitsFast(); + + //gl requirements + void checkGlCaps(); + void checkGlOptionalCaps(); + + //gl init + void init3dList(); + void init2dList(); + void init3dListMenu(MainMenu *mm); + + //misc + void loadProjectionMatrix(); + void enableProjectiveTexturing(); + + //private aux drawing + void renderSelectionCircle(Vec3f v, int size, float radius); + void renderArrow(const Vec3f &pos1, const Vec3f &pos2, const Vec3f &color, float width); + void renderProgressBar(int size, int x, int y, Font2D *font); + void renderTile(const Vec2i &pos); + void renderQuad(int x, int y, int w, int h, const Texture2D *texture); + + //static + static Texture2D::Filter strToTextureFilter(const string &s); +}; + +}} //end namespace + +#endif diff --git a/source/glest_game/gui/gui.cpp b/source/glest_game/gui/gui.cpp index d68d61a3..65adda3a 100644 --- a/source/glest_game/gui/gui.cpp +++ b/source/glest_game/gui/gui.cpp @@ -90,6 +90,8 @@ void SelectionQuad::disable(){ Gui::Gui(){ if(Socket::enableDebugText) printf("In [%s::%s] START\n",__FILE__,__FUNCTION__); + allowRotateUnits = Config::getInstance().getBool("AllowRotateUnits","0"); + posObjWorld= Vec2i(54, 14); computeSelection= false; validPosObjWorld= false; @@ -309,11 +311,11 @@ void Gui::groupKey(int groupIndex){ } } -float Gui::getUnitTypeBuildRotation(int unitId) const { +float Gui::getUnitTypeBuildRotation(string unitKey) const { float rotationValue = -1; - if(unitTypeBuildRotation.find(unitId) != unitTypeBuildRotation.end()) { - rotationValue = unitTypeBuildRotation.find(unitId)->second; + if(unitTypeBuildRotation.find(unitKey) != unitTypeBuildRotation.end()) { + rotationValue = unitTypeBuildRotation.find(unitKey)->second; } return rotationValue; @@ -334,11 +336,14 @@ void Gui::hotKey(char key){ } else if(key=='R'){ //!!! - if(0 && isPlacingBuilding()) { + if(allowRotateUnits == true && isPlacingBuilding()) { const UnitType *unitType = getBuilding(); - float unitTypeRotation = getUnitTypeBuildRotation(unitType->getId()); + int factionIndex = world->getThisFactionIndex(); + char unitKey[50]=""; + sprintf(unitKey,"%d_%d",unitType->getId(),factionIndex); + float unitTypeRotation = getUnitTypeBuildRotation(unitKey); - if(Socket::enableDebugText) printf("In [%s::%s] unitType->getId() = %d unitTypeRotation = %f\n",__FILE__,__FUNCTION__,unitType->getId(),unitTypeRotation); + if(Socket::enableDebugText) printf("In [%s::%s] factionIndex = %d unitType->getId() = %d unitTypeRotation = %f\n",__FILE__,__FUNCTION__,factionIndex,unitType->getId(),unitTypeRotation); if(unitTypeRotation < 0) { unitTypeRotation = 0; @@ -347,7 +352,7 @@ void Gui::hotKey(char key){ if(unitTypeRotation >= 360) { unitTypeRotation = 0; } - unitTypeBuildRotation[unitType->getId()] = unitTypeRotation; + unitTypeBuildRotation[unitKey] = unitTypeRotation; if(Socket::enableDebugText) printf("In [%s::%s] unitType->getId() = %d NEW unitTypeRotation = %f\n",__FILE__,__FUNCTION__,unitType->getId(),unitTypeRotation); } diff --git a/source/glest_game/gui/gui.h b/source/glest_game/gui/gui.h new file mode 100644 index 00000000..70806390 --- /dev/null +++ b/source/glest_game/gui/gui.h @@ -0,0 +1,207 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_GUI_H_ +#define _GLEST_GAME_GUI_H_ + +#include "resource.h" +#include "command_type.h" +#include "display.h" +#include "commander.h" +#include "console.h" +#include "selection.h" +#include "random.h" +#include + +using Shared::Util::Random; + +namespace Glest{ namespace Game{ + +class Unit; +class World; +class CommandType; +class GameCamera; +class Game; + +enum DisplayState{ + dsEmpty, + dsUnitSkills, + dsUnitBuild, + dsEnemy +}; + +// ===================================================== +// class Mouse3d +// ===================================================== + +class Mouse3d{ +public: + static const float fadeSpeed; + +private: + bool enabled; + int rot; + float fade; + +public: + Mouse3d(); + + void enable(); + void update(); + + bool isEnabled() const {return enabled;} + float getFade() const {return fade;} + int getRot() const {return rot;} +}; + +// ===================================================== +// class SelectionQuad +// ===================================================== + +class SelectionQuad{ +private: + Vec2i posDown; + Vec2i posUp; + bool enabled; + +public: + SelectionQuad(); + + bool isEnabled() const {return enabled;} + Vec2i getPosDown() const {return posDown;} + Vec2i getPosUp() const {return posUp;} + + void setPosDown(const Vec2i &posDown); + void setPosUp(const Vec2i &posUp); + void disable(); +}; + +// ===================================================== +// class Gui +// +/// In game GUI +// ===================================================== + +class Gui{ +public: + static const int maxSelBuff= 128*5; + static const int upgradeDisplayIndex= 8; + static const int cancelPos= 15; + static const int meetingPointPos= 14; + static const int imageCount= 16; + static const int invalidPos= -1; + static const int doubleClickSelectionRadius= 20; + +private: + //External objects + Random random; + const Commander *commander; + const World *world; + GameCamera *gameCamera; + Console *console; + + //Positions + Vec2i posObjWorld; //world coords + bool validPosObjWorld; + bool computeSelection; + + //display + const UnitType *choosenBuildingType; + const CommandType *activeCommandType; + CommandClass activeCommandClass; + int activePos; + + //composite + Display display; + Mouse3d mouse3d; + Selection selection; + SelectionQuad selectionQuad; + + //states + bool selectingBuilding; + bool selectingPos; + bool selectingMeetingPoint; + + bool allowRotateUnits; + std::map unitTypeBuildRotation; + +public: + Gui(); + void init(Game *game); + void end(); + + //get + Vec2i getPosObjWorld() const {return posObjWorld;} + const UnitType *getBuilding() const; + + const Mouse3d *getMouse3d() const {return &mouse3d;} + const Display *getDisplay() const {return &display;} + const Selection *getSelection() const {return &selection;} + const SelectionQuad *getSelectionQuad() const {return &selectionQuad;} + bool isSelected(const Unit *unit) const {return selection.hasUnit(unit);} + + bool isValidPosObjWorld() const {return validPosObjWorld;} + bool isSelecting() const {return selectionQuad.isEnabled();} + bool isSelectingPos() const {return selectingPos;} + bool isSelectingBuilding() const {return selectingBuilding;} + bool isPlacingBuilding() const; + + //set + void invalidatePosObjWorld(); + void setComputeSelectionFlag(); + + //events + void update(); + void tick(); + bool mouseValid(int x, int y); + void mouseDownLeftDisplay(int x, int y); + void mouseMoveDisplay(int x, int y); + void mouseDownLeftGraphics(int x, int y); + void mouseDownRightGraphics(int x, int y); + void mouseUpLeftGraphics(int x, int y); + void mouseMoveGraphics(int x, int y); + void mouseDoubleClickLeftGraphics(int x, int y); + void groupKey(int groupIndex); + void hotKey(char key); + + //misc + void onSelectionChanged(); + + float getUnitTypeBuildRotation(string unitKey) const; + +private: + + //orders + void giveDefaultOrders(int x, int y); + void giveOneClickOrders(); + void giveTwoClickOrders(int x, int y); + + //hotkeys + void centerCameraOnSelection(); + void selectInterestingUnit(InterestingUnitType iut); + void clickCommonCommand(CommandClass commandClass); + + //misc + int computePosDisplay(int x, int y); + void computeDisplay(); + void resetState(); + void mouseDownDisplayUnitSkills(int posDisplay); + void mouseDownDisplayUnitBuild(int posDisplay); + void computeInfoString(int posDisplay); + void addOrdersResultToConsole(CommandClass cc, CommandResult rr); + bool isSharedCommandClass(CommandClass commandClass); + void computeSelected(bool doubleCkick); + bool computeTarget(const Vec2i &screenPos, Vec2i &targetPos, const Unit *&targetUnit); +}; + +}} //end namespace + +#endif diff --git a/source/glest_game/main/program.cpp b/source/glest_game/main/program.cpp new file mode 100644 index 00000000..50992e99 --- /dev/null +++ b/source/glest_game/main/program.cpp @@ -0,0 +1,270 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "program.h" + +#include "sound.h" +#include "renderer.h" +#include "config.h" +#include "game.h" +#include "main_menu.h" +#include "intro.h" +#include "world.h" +#include "main.h" +#include "sound_renderer.h" +#include "logger.h" +#include "profiler.h" +#include "core_data.h" +#include "metrics.h" +#include "network_manager.h" +#include "menu_state_custom_game.h" +#include "menu_state_join_game.h" +#include "leak_dumper.h" + +using namespace Shared::Util; +using namespace Shared::Graphics; +using namespace Shared::Graphics::Gl; + +// ===================================================== +// class Program +// ===================================================== + +namespace Glest{ namespace Game{ + +const int Program::maxTimes= 10; + +// ===================== PUBLIC ======================== + +Program::Program(){ + programState= NULL; +} + +void Program::initNormal(WindowGl *window){ + init(window); + setState(new Intro(this)); +} + +void Program::initServer(WindowGl *window){ + MainMenu* mainMenu= NULL; + + init(window); + mainMenu= new MainMenu(this); + setState(mainMenu); + mainMenu->setState(new MenuStateCustomGame(this, mainMenu, true)); +} + +void Program::initClient(WindowGl *window, const Ip &serverIp){ + MainMenu* mainMenu= NULL; + + init(window); + mainMenu= new MainMenu(this); + setState(mainMenu); + mainMenu->setState(new MenuStateJoinGame(this, mainMenu, true, serverIp)); +} + +Program::~Program(){ + delete programState; + + Renderer::getInstance().end(); + + //restore video mode + restoreDisplaySettings(); +} + +void Program::mouseDownLeft(int x, int y){ + const Metrics &metrics= Metrics::getInstance(); + programState->mouseDownLeft(metrics.toVirtualX(x), metrics.toVirtualY(y)); +} + +void Program::mouseUpLeft(int x, int y){ + const Metrics &metrics= Metrics::getInstance(); + programState->mouseUpLeft(metrics.toVirtualX(x), metrics.toVirtualY(y)); +} + +void Program::mouseDownRight(int x, int y){ + const Metrics &metrics= Metrics::getInstance(); + programState->mouseDownRight(metrics.toVirtualX(x), metrics.toVirtualY(y)); +} + +void Program::mouseDoubleClickLeft(int x, int y){ + const Metrics &metrics= Metrics::getInstance(); + programState->mouseDoubleClickLeft(metrics.toVirtualX(x), metrics.toVirtualY(y)); +} + +void Program::mouseMove(int x, int y, const MouseState *ms){ + const Metrics &metrics= Metrics::getInstance(); + programState->mouseMove(metrics.toVirtualX(x), metrics.toVirtualY(y), ms); +} + +void Program::keyDown(char key){ + //delegate event + programState->keyDown(key); +} + +void Program::keyUp(char key){ + programState->keyUp(key); +} + +void Program::keyPress(char c){ + programState->keyPress(c); +} + +void Program::loop(){ + + //render + programState->render(); + + //update camera + while(updateCameraTimer.isTime()){ + programState->updateCamera(); + } + + //update world + while(updateTimer.isTime()){ + GraphicComponent::update(); + programState->update(); + SoundRenderer::getInstance().update(); + NetworkManager::getInstance().update(); + } + + //fps timer + while(fpsTimer.isTime()){ + programState->tick(); + } +} + +void Program::resize(SizeState sizeState){ + + switch(sizeState){ + case ssMinimized: + //restoreVideoMode(); + break; + case ssMaximized: + case ssRestored: + //setDisplaySettings(); + //renderer.reloadResources(); + break; + } +} + +// ==================== misc ==================== + +void Program::setState(ProgramState *programState) +{ + + if(Socket::enableDebugText) printf("In [%s::%s] START\n",__FILE__,__FUNCTION__); + + delete this->programState; + + if(Socket::enableDebugText) printf("In [%s::%s] %d\n",__FILE__,__FUNCTION__,__LINE__); + + this->programState= programState; + programState->load(); + + if(Socket::enableDebugText) printf("In [%s::%s] %d\n",__FILE__,__FUNCTION__,__LINE__); + + programState->init(); + + if(Socket::enableDebugText) printf("In [%s::%s] %d\n",__FILE__,__FUNCTION__,__LINE__); + + updateTimer.reset(); + updateCameraTimer.reset(); + fpsTimer.reset(); + + if(Socket::enableDebugText) printf("In [%s::%s] END\n",__FILE__,__FUNCTION__); +} + +void Program::exit(){ + window->destroy(); +} + +// ==================== PRIVATE ==================== + +void Program::init(WindowGl *window){ + + this->window= window; + Config &config= Config::getInstance(); + + //set video mode + setDisplaySettings(); + + //window + window->setText("Glest"); + window->setStyle(config.getBool("Windowed")? wsWindowedFixed: wsFullscreen); + window->setPos(0, 0); + window->setSize(config.getInt("ScreenWidth"), config.getInt("ScreenHeight")); + window->create(); + + //timers + fpsTimer.init(1, maxTimes); + updateTimer.init(GameConstants::updateFps, maxTimes); + updateCameraTimer.init(GameConstants::cameraFps, maxTimes); + + //log start + Logger &logger= Logger::getInstance(); + logger.setFile("glest.log"); + logger.clear(); + + //lang + Lang &lang= Lang::getInstance(); + lang.loadStrings(config.getString("Lang")); + + //render + Renderer &renderer= Renderer::getInstance(); + + window->initGl(config.getInt("ColorBits"), config.getInt("DepthBits"), config.getInt("StencilBits")); + window->makeCurrentGl(); + + //coreData, needs renderer, but must load before renderer init + CoreData &coreData= CoreData::getInstance(); + coreData.load(); + + //init renderer (load global textures) + renderer.init(); + + //sound + SoundRenderer &soundRenderer= SoundRenderer::getInstance(); + soundRenderer.init(window); + + NetworkInterface::setAllowGameDataSynchCheck(Config::getInstance().getBool("AllowGameDataSynchCheck","0")); + NetworkInterface::setAllowDownloadDataSynch(Config::getInstance().getBool("AllowDownloadDataSynch","0")); +} + +void Program::setDisplaySettings(){ + + Config &config= Config::getInstance(); + + if(!config.getBool("Windowed")){ + + int freq= config.getInt("RefreshFrequency"); + int colorBits= config.getInt("ColorBits"); + int screenWidth= config.getInt("ScreenWidth"); + int screenHeight= config.getInt("ScreenHeight"); + + if(!(changeVideoMode(screenWidth, screenHeight, colorBits, freq) || + changeVideoMode(screenWidth, screenHeight, colorBits, 0))) + { + throw runtime_error( + "Error setting video mode: " + + intToStr(screenWidth) + "x" + intToStr(screenHeight) + "x" + intToStr(colorBits)); + } + } +} + +void Program::restoreDisplaySettings(){ + Config &config= Config::getInstance(); + + if(!config.getBool("Windowed")){ + restoreVideoMode(); + } +} + +}}//end namespace diff --git a/source/glest_game/network/network_interface.h b/source/glest_game/network/network_interface.h new file mode 100644 index 00000000..a077a365 --- /dev/null +++ b/source/glest_game/network/network_interface.h @@ -0,0 +1,211 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_NETWORKINTERFACE_H_ +#define _GLEST_GAME_NETWORKINTERFACE_H_ + +#include +#include + +#include "checksum.h" +#include "network_message.h" +#include "network_types.h" + +#include "game_settings.h" + +#include "thread.h" +#include "types.h" + +using std::string; +using std::vector; +using Shared::Util::Checksum; +using namespace Shared::Util; +using namespace Shared::Platform; + +namespace Glest{ namespace Game{ + +//class GameSettings; + +// ===================================================== +// class NetworkInterface +// ===================================================== + +class NetworkInterface { + +protected: + static bool allowGameDataSynchCheck; + static bool allowDownloadDataSynch; + bool networkGameDataSynchCheckOkMap; + bool networkGameDataSynchCheckOkTile; + bool networkGameDataSynchCheckOkTech; + bool networkGameDataSynchCheckOkFogOfWar; + + string chatText; + string chatSender; + int chatTeamIndex; + +public: + static const int readyWaitTimeout; + GameSettings gameSettings; + +public: + virtual ~NetworkInterface(){} + + virtual Socket* getSocket()= 0; + virtual const Socket* getSocket() const= 0; + virtual void close()= 0; + + string getIp() const {return getSocket()->getIp();} + string getHostName() const {return getSocket()->getHostName();} + + void sendMessage(const NetworkMessage* networkMessage); + NetworkMessageType getNextMessageType(bool checkHasDataFirst = false); + bool receiveMessage(NetworkMessage* networkMessage); + + bool isConnected(); + + //virtual void setGameSettings(GameSettings *serverGameSettings) { gameSettings = *serverGameSettings; } + const virtual GameSettings * getGameSettings() { return &gameSettings; } + + static void setAllowDownloadDataSynch(bool value) { allowDownloadDataSynch = value; } + static bool getAllowDownloadDataSynch() { return allowDownloadDataSynch; } + + static void setAllowGameDataSynchCheck(bool value) { allowGameDataSynchCheck = value; } + static bool getAllowGameDataSynchCheck() { return allowGameDataSynchCheck; } + + virtual bool getNetworkGameDataSynchCheckOk() { return (networkGameDataSynchCheckOkMap && networkGameDataSynchCheckOkTile && networkGameDataSynchCheckOkTech && networkGameDataSynchCheckOkFogOfWar); } + virtual void setNetworkGameDataSynchCheckOkMap(bool value) { networkGameDataSynchCheckOkMap = value; } + virtual void setNetworkGameDataSynchCheckOkTile(bool value) { networkGameDataSynchCheckOkTile = value; } + virtual void setNetworkGameDataSynchCheckOkTech(bool value) { networkGameDataSynchCheckOkTech = value; } + virtual bool getNetworkGameDataSynchCheckOkMap() { return networkGameDataSynchCheckOkMap; } + virtual bool getNetworkGameDataSynchCheckOkTile() { return networkGameDataSynchCheckOkTile; } + virtual bool getNetworkGameDataSynchCheckOkTech() { return networkGameDataSynchCheckOkTech; } + virtual bool getFogOfWar()=0; + virtual bool getNetworkGameDataSynchCheckOkFogOfWar() { return networkGameDataSynchCheckOkFogOfWar; } + virtual void setNetworkGameDataSynchCheckOkFogOfWar(bool value) { networkGameDataSynchCheckOkFogOfWar = value; } + + const string getChatText() const {return chatText;} + const string getChatSender() const {return chatSender;} + int getChatTeamIndex() const {return chatTeamIndex;} +}; + +// ===================================================== +// class GameNetworkInterface +// +// Adds functions common to servers and clients +// but not connection slots +// ===================================================== + +class GameNetworkInterface: public NetworkInterface{ +private: + typedef vector Commands; + +protected: + Commands requestedCommands; //commands requested by the user + Commands pendingCommands; //commands ready to be given + bool quit; + +public: + GameNetworkInterface(); + + //message processimg + virtual void update()= 0; + virtual void updateLobby()= 0; + virtual void updateKeyframe(int frameCount)= 0; + virtual void waitUntilReady(Checksum* checksum)= 0; + + //message sending + virtual void sendTextMessage(const string &text, int teamIndex)= 0; + virtual void quitGame(bool userManuallyQuit)=0; + + //misc + virtual string getNetworkStatus() const= 0; + + //access functions + void requestCommand(const NetworkCommand *networkCommand) {requestedCommands.push_back(*networkCommand);} + int getPendingCommandCount() const {return pendingCommands.size();} + const NetworkCommand* getPendingCommand(int i) const {return &pendingCommands[i];} + void clearPendingCommands() {pendingCommands.clear();} + bool getQuit() const {return quit;} +}; + +// ===================================================== +// class FileTransferSocketThread +// ===================================================== + +enum FileTransferHostType +{ + eClient, + eServer +}; + +enum FileTransferOperationType +{ + eSend, + eReceive +}; + +class FileTransferInfo +{ +private: + + void CopyAll(const FileTransferInfo &obj) + { + hostType = obj.hostType; + serverIP = obj.serverIP; + serverPort = obj.serverPort; + opType = obj.opType; + fileName = obj.fileName; + } + +public: + FileTransferInfo() + { + } + FileTransferInfo(const FileTransferInfo &obj) + { + CopyAll(obj); + } + FileTransferInfo &operator=(const FileTransferInfo &obj) + { + CopyAll(obj); + return *this; + } + + FileTransferHostType hostType; + string serverIP; + int32 serverPort; + FileTransferOperationType opType; + string fileName; +}; + +class FileInfo +{ +public: + string fileName; + int64 filesize; + int32 filecrc; +}; + +class FileTransferSocketThread : public Thread +{ +private: + FileTransferInfo info; + +public: + FileTransferSocketThread(FileTransferInfo fileInfo); + virtual void execute(); +}; + + +}}//end namespace + +#endif diff --git a/source/glest_game/type_instances/unit.cpp b/source/glest_game/type_instances/unit.cpp index 988056ac..c792da6a 100644 --- a/source/glest_game/type_instances/unit.cpp +++ b/source/glest_game/type_instances/unit.cpp @@ -107,6 +107,7 @@ const int Unit::invalidId= -1; Unit::Unit(int id, const Vec2i &pos, const UnitType *type, Faction *faction, Map *map, float unitPlacementRotation) { //if(Socket::enableDebugText) printf("In [%s::%s] START\n",__FILE__,__FUNCTION__); + allowRotateUnits = Config::getInstance().getBool("AllowRotateUnits","0"); Random random; @@ -1108,69 +1109,76 @@ void Unit::startDamageParticles(){ bool Unit::getCellMapCell(int x, int y) const { const UnitType *ut= getType(); - if(ut != NULL && rotateAmount > 0) { + + if(allowRotateUnits == true && ut != NULL && rotateAmount > 0) { return cellMap[ut->getSize() * y + x]; } - else { + else if(ut != NULL) { return ut->getCellMapCell(x,y); } + else { + throw runtime_error("ut == NULL in Unit::getCellMapCell()!"); + } } void Unit::setRotateAmount(float value) { - rotateAmount = value; + if(allowRotateUnits == true) { + rotateAmount = value; + //if(Socket::enableDebugText) printf("In [%s::%s] unit id = %d [%s] rotate amount = %f\n",__FILE__,__FUNCTION__,getId(), getFullName().c_str(),rotateAmount); - //if(Socket::enableDebugText) printf("In [%s::%s] unit id = %d [%s] rotate amount = %f\n",__FILE__,__FUNCTION__,getId(), getFullName().c_str(),rotateAmount); + const UnitType *ut= getType(); + if(ut != NULL && ut->hasCellMap() == true) { + int matrixSize = ut->getSize(); - const UnitType *ut= getType(); - if(ut != NULL && ut->hasCellMap() == true) { - int matrixSize = ut->getSize(); + if(rotateAmount > 0) { - if(rotateAmount > 0) { + delete [] cellMap; + cellMap = new bool[matrixSize * matrixSize]; - delete [] cellMap; - cellMap = new bool[matrixSize * matrixSize]; + for(int iRow = 0; iRow < matrixSize; ++iRow) { + for(int iCol = 0; iCol < matrixSize; ++iCol) { + bool getCellResult = ut->getCellMapCell(iCol, iRow); + //if(Socket::enableDebugText) printf("In [%s::%s] [%d,%d] = %d\n",__FILE__,__FUNCTION__,iRow,iCol,getCellResult); - for(int iRow = 0; iRow < matrixSize; ++iRow) { - for(int iCol = 0; iCol < matrixSize; ++iCol) { - bool getCellResult = ut->getCellMapCell(iCol, iRow); - //if(Socket::enableDebugText) printf("In [%s::%s] [%d,%d] = %d\n",__FILE__,__FUNCTION__,iRow,iCol,getCellResult); + int newRow = 0; + int newCol = 0; - int newRow = 0; - int newCol = 0; + switch((int)rotateAmount) + { + case 90: + newRow = (matrixSize - iCol - 1); + newCol = iRow; + break; + case 180: + newRow = (matrixSize - iRow - 1); + newCol = (matrixSize - iCol - 1); + break; + case 270: + newRow = iCol; + newCol = (matrixSize - iRow - 1); + break; - switch((int)rotateAmount) - { - case 90: - newRow = (matrixSize - iCol - 1); - newCol = iRow; - break; - case 180: - newRow = (matrixSize - iRow - 1); - newCol = (matrixSize - iCol - 1); - break; - case 270: - newRow = iCol; - newCol = (matrixSize - iRow - 1); - break; + } + //if(Socket::enableDebugText) printf("In [%s::%s] ABOUT TO Transform to [%d,%d] = %d\n",__FILE__,__FUNCTION__,newRow,newCol,getCellResult); + + // bool getCellMapCell(int x, int y) const {return cellMap[size*y+x];} + // cellMap[i*size+j]= row[j]=='0'? false: true; + cellMap[matrixSize * newRow + newCol] = getCellResult; } - - //if(Socket::enableDebugText) printf("In [%s::%s] ABOUT TO Transform to [%d,%d] = %d\n",__FILE__,__FUNCTION__,newRow,newCol,getCellResult); - - // bool getCellMapCell(int x, int y) const {return cellMap[size*y+x];} - // cellMap[i*size+j]= row[j]=='0'? false: true; - cellMap[matrixSize * newRow + newCol] = getCellResult; } } - } - //if(Socket::enableDebugText) printf("In [%s::%s] Transformed matrix below:\n",__FILE__,__FUNCTION__); - for(int iRow = 0; iRow < matrixSize; ++iRow) { - for(int iCol = 0; iCol < matrixSize; ++iCol) { - bool getCellResult = ut->getCellMapCell(iCol, iRow); - bool getCellResultRotated = getCellMapCell(iRow, iCol); - //if(Socket::enableDebugText) printf("In [%s::%s] matrix [%d,%d] = %d, rotated = %d\n",__FILE__,__FUNCTION__,iRow,iCol,getCellResult,getCellResultRotated); + //if(Socket::enableDebugText) printf("In [%s::%s] Transformed matrix below:\n",__FILE__,__FUNCTION__); + /* + for(int iRow = 0; iRow < matrixSize; ++iRow) { + for(int iCol = 0; iCol < matrixSize; ++iCol) { + bool getCellResult = ut->getCellMapCell(iCol, iRow); + bool getCellResultRotated = getCellMapCell(iRow, iCol); + if(Socket::enableDebugText) printf("In [%s::%s] matrix [%d,%d] = %d, rotated = %d\n",__FILE__,__FUNCTION__,iRow,iCol,getCellResult,getCellResultRotated); + } } + */ } } } diff --git a/source/glest_game/type_instances/unit.h b/source/glest_game/type_instances/unit.h new file mode 100644 index 00000000..0e53c37a --- /dev/null +++ b/source/glest_game/type_instances/unit.h @@ -0,0 +1,316 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Marti�o Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_UNIT_H_ +#define _GLEST_GAME_UNIT_H_ + +#include "model.h" +#include "upgrade_type.h" +#include "particle.h" +#include "skill_type.h" + +namespace Glest{ namespace Game{ + +using Shared::Graphics::ParticleSystem; +using Shared::Graphics::UnitParticleSystem; +using Shared::Graphics::Vec4f; +using Shared::Graphics::Vec2f; +using Shared::Graphics::Vec3f; +using Shared::Graphics::Vec2i; +using Shared::Graphics::Model; + +class Map; +class Faction; +class Unit; +class Command; +class SkillType; +class ResourceType; +class CommandType; +class SkillType; +class UnitType; +class TotalUpgrade; +class UpgradeType; +class Level; +class MorphCommandType; + +enum CommandResult{ + crSuccess, + crFailRes, + crFailReqs, + crFailUndefined, + crSomeFailed +}; + +enum InterestingUnitType{ + iutIdleHarvester, + iutBuiltBuilding, + iutProducer, + iutDamaged, + iutStore +}; + +// ===================================================== +// class UnitObserver +// ===================================================== + +class UnitObserver{ +public: + enum Event{ + eKill + }; + +public: + virtual ~UnitObserver() {} + virtual void unitEvent(Event event, const Unit *unit)=0; +}; + +// ===================================================== +// class UnitReference +// ===================================================== + +class UnitReference{ +private: + int id; + Faction *faction; + +public: + UnitReference(); + + void operator=(const Unit *unit); + Unit *getUnit() const; +}; + +// ===================================================== +// class UnitPath +// +/// Holds the next cells of a Unit movement +// ===================================================== + +class UnitPath{ +private: + static const int maxBlockCount; + +private: + int blockCount; + vector pathQueue; + +public: + bool isBlocked(); + bool isEmpty(); + + void clear(); + void incBlockCount(); + void push(const Vec2i &path); + Vec2i pop(); +}; + +// =============================== +// class Unit +// +/// A game unit or building +// =============================== + +class Unit{ +private: + typedef list Commands; + typedef list Observers; + typedef list UnitParticleSystems; + +public: + static const float speedDivider; + static const int maxDeadCount; + static const float highlightTime; + static const int invalidId; + + +private: + int id; + int hp; + int ep; + int loadCount; + int deadCount; + float progress; //between 0 and 1 + float lastAnimProgress; //between 0 and 1 + float animProgress; //between 0 and 1 + float highlight; + int progress2; + int kills; + + UnitReference targetRef; + + Field currField; + Field targetField; + const Level *level; + + Vec2i pos; + Vec2i lastPos; + Vec2i targetPos; //absolute target pos + Vec3f targetVec; + Vec2i meetingPos; + + float lastRotation; //in degrees + float targetRotation; + float rotation; + + const UnitType *type; + const ResourceType *loadType; + const SkillType *currSkill; + + bool toBeUndertaken; + bool alive; + bool showUnitParticles; + + Faction *faction; + ParticleSystem *fire; + TotalUpgrade totalUpgrade; + Map *map; + + UnitPath unitPath; + + Commands commands; + Observers observers; + UnitParticleSystems unitParticleSystems; + UnitParticleSystems damageParticleSystems; + + bool allowRotateUnits; + float rotateAmount; + bool *cellMap; + +public: + Unit(int id, const Vec2i &pos, const UnitType *type, Faction *faction, Map *map, float unitPlacementRotation); + ~Unit(); + + //queries + int getId() const {return id;} + Field getCurrField() const {return currField;} + int getLoadCount() const {return loadCount;} + float getLastAnimProgress() const {return lastAnimProgress;} + float getProgress() const {return progress;} + float getAnimProgress() const {return animProgress;} + float getHightlight() const {return highlight;} + int getProgress2() const {return progress2;} + int getFactionIndex() const; + int getTeam() const; + int getHp() const {return hp;} + int getEp() const {return ep;} + int getProductionPercent() const; + float getHpRatio() const; + float getEpRatio() const; + bool getToBeUndertaken() const {return toBeUndertaken;} + Vec2i getTargetPos() const {return targetPos;} + Vec3f getTargetVec() const {return targetVec;} + Field getTargetField() const {return targetField;} + Vec2i getMeetingPos() const {return meetingPos;} + Faction *getFaction() const {return faction;} + const ResourceType *getLoadType() const {return loadType;} + const UnitType *getType() const {return type;} + const SkillType *getCurrSkill() const {return currSkill;} + const TotalUpgrade *getTotalUpgrade() const {return &totalUpgrade;} + float getRotation() const {return rotation;} + float getVerticalRotation() const; + ParticleSystem *getFire() const {return fire;} + int getKills() {return kills;} + const Level *getLevel() const {return level;} + const Level *getNextLevel() const; + string getFullName() const; + const UnitPath *getPath() const {return &unitPath;} + UnitPath *getPath() {return &unitPath;} + + //pos + Vec2i getPos() const {return pos;} + Vec2i getLastPos() const {return lastPos;} + Vec2i getCenteredPos() const; + Vec2f getFloatCenteredPos() const; + Vec2i getCellPos() const; + + //is + bool isHighlighted() const {return highlight>0.f;} + bool isDead() const {return !alive;} + bool isAlive() const {return alive;} + bool isOperative() const; + bool isBeingBuilt() const; + bool isBuilt() const; + bool isPutrefacting() const; + bool isAlly(const Unit *unit) const; + bool isDamaged() const; + bool isInteresting(InterestingUnitType iut) const; + + //set + void setCurrField(Field currField) {this->currField= currField;} + void setCurrSkill(const SkillType *currSkill); + void setCurrSkill(SkillClass sc); + void setLoadCount(int loadCount) {this->loadCount= loadCount;} + void setLoadType(const ResourceType *loadType) {this->loadType= loadType;} + void setProgress2(int progress2) {this->progress2= progress2;} + void setPos(const Vec2i &pos); + void setTargetPos(const Vec2i &targetPos); + void setTarget(const Unit *unit); + void setTargetVec(const Vec3f &targetVec) {this->targetVec= targetVec;} + void setMeetingPos(const Vec2i &meetingPos) {this->meetingPos= meetingPos;} + void setVisible(const bool visible); + + //render related + const Model *getCurrentModel() const; + Vec3f getCurrVector() const; + Vec3f getCurrVectorFlat() const; + + //command related + bool anyCommand() const; + Command *getCurrCommand() const; + unsigned int getCommandSize() const; + CommandResult giveCommand(Command *command); //give a command + CommandResult finishCommand(); //command finished + CommandResult cancelCommand(); //cancel canceled + + //lifecycle + void create(bool startingUnit= false); + void born(); + void kill(); + void undertake(); + + //observers + void addObserver(UnitObserver *unitObserver) ; + void removeObserver(UnitObserver *unitObserver); + void notifyObservers(UnitObserver::Event event); + + //other + void resetHighlight(); + const CommandType *computeCommandType(const Vec2i &pos, const Unit *targetUnit= NULL) const; + string getDesc() const; + bool computeEp(); + bool repair(); + bool decHp(int i); + int update2(); + bool update(); + void tick(); + void applyUpgrade(const UpgradeType *upgradeType); + void computeTotalUpgrade(); + void incKills(); + bool morph(const MorphCommandType *mct); + CommandResult checkCommand(Command *command) const; + void applyCommand(Command *command); + + void setRotateAmount(float value); + float getRotateAmount() { return rotateAmount; } + bool getCellMapCell(int x, int y) const; + +private: + float computeHeight(const Vec2i &pos) const; + void updateTarget(); + void clearCommands(); + CommandResult undoCommand(Command *command); + void stopDamageParticles(); + void startDamageParticles(); +}; + +}}// end namespace + +#endif diff --git a/source/glest_game/world/unit_updater.cpp b/source/glest_game/world/unit_updater.cpp new file mode 100644 index 00000000..435b2dcc --- /dev/null +++ b/source/glest_game/world/unit_updater.cpp @@ -0,0 +1,898 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Marti�o Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "unit_updater.h" + +#include +#include + +#include "sound.h" +#include "upgrade.h" +#include "unit.h" +#include "particle_type.h" +#include "core_data.h" +#include "config.h" +#include "renderer.h" +#include "sound_renderer.h" +#include "game.h" +#include "path_finder.h" +#include "object.h" +#include "faction.h" +#include "network_manager.h" +#include "leak_dumper.h" + +using namespace Shared::Graphics; +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class UnitUpdater +// ===================================================== + +// ===================== PUBLIC ======================== + +void UnitUpdater::init(Game *game){ + + this->gui= game->getGui(); + this->gameCamera= game->getGameCamera(); + this->world= game->getWorld(); + this->map= world->getMap(); + this->console= game->getConsole(); + this->scriptManager= game->getScriptManager(); + pathFinder.init(map); + + allowRotateUnits = Config::getInstance().getBool(reinterpret_cast("AllowRotateUnits"),"0"); +} + + +// ==================== progress skills ==================== + +//skill dependent actions +void UnitUpdater::updateUnit(Unit *unit){ + + SoundRenderer &soundRenderer= SoundRenderer::getInstance(); + + //play skill sound + const SkillType *currSkill= unit->getCurrSkill(); + if(currSkill->getSound()!=NULL){ + float soundStartTime= currSkill->getSoundStartTime(); + if(soundStartTime>=unit->getLastAnimProgress() && soundStartTimegetAnimProgress()){ + if(map->getSurfaceCell(Map::toSurfCoords(unit->getPos()))->isVisible(world->getThisTeamIndex())){ + soundRenderer.playFx(currSkill->getSound(), unit->getCurrVector(), gameCamera->getPos()); + } + } + } + + //start attack particle system + if(unit->getCurrSkill()->getClass()==scAttack){ + const AttackSkillType *ast= static_cast(unit->getCurrSkill()); + float attackStartTime= ast->getAttackStartTime(); + if(attackStartTime>=unit->getLastAnimProgress() && attackStartTimegetAnimProgress()){ + startAttackParticleSystem(unit); + } + } + + //update unit + if(unit->update()){ + + updateUnitCommand(unit); + + //if unit is out of EP, it stops + if(unit->computeEp()){ + unit->setCurrSkill(scStop); + unit->cancelCommand(); + } + + //move unit in cells + if(unit->getCurrSkill()->getClass()==scMove){ + world->moveUnitCells(unit); + + //play water sound + if(map->getCell(unit->getPos())->getHeight()getWaterLevel() && unit->getCurrField()==fLand){ + soundRenderer.playFx(CoreData::getInstance().getWaterSound()); + } + } + } + + //unit death + if(unit->isDead() && unit->getCurrSkill()->getClass()!=scDie){ + unit->kill(); + } +} + + +// ==================== progress commands ==================== + +//VERY IMPORTANT: compute next state depending on the first order of the list +void UnitUpdater::updateUnitCommand(Unit *unit){ + + //if unis has command process it + if(unit->anyCommand()) { + unit->getCurrCommand()->getCommandType()->update(this, unit); + } + + //if no commands stop and add stop command + if(!unit->anyCommand() && unit->isOperative()){ + unit->setCurrSkill(scStop); + if(unit->getType()->hasCommandClass(ccStop)){ + unit->giveCommand(new Command(unit->getType()->getFirstCtOfClass(ccStop))); + } + } +} + +// ==================== updateStop ==================== + +void UnitUpdater::updateStop(Unit *unit){ + + Command *command= unit->getCurrCommand(); + const StopCommandType *sct = static_cast(command->getCommandType()); + Unit *sighted; + + unit->setCurrSkill(sct->getStopSkillType()); + + //we can attack any unit => attack it + if(unit->getType()->hasSkillClass(scAttack)){ + for(int i=0; igetType()->getCommandTypeCount(); ++i){ + const CommandType *ct= unit->getType()->getCommandType(i); + + //look for an attack skill + const AttackSkillType *ast= NULL; + if(ct->getClass()==ccAttack){ + ast= static_cast(ct)->getAttackSkillType(); + } + else if(ct->getClass()==ccAttackStopped){ + ast= static_cast(ct)->getAttackSkillType(); + } + + //use it to attack + if(ast!=NULL){ + if(attackableOnSight(unit, &sighted, ast)){ + unit->giveCommand(new Command(ct, sighted->getPos())); + break; + } + } + } + } + + //see any unit and cant attack it => run + else if(unit->getType()->hasCommandClass(ccMove)){ + if(attackerOnSight(unit, &sighted)){ + Vec2i escapePos= unit->getPos()*2-sighted->getPos(); + unit->giveCommand(new Command(unit->getType()->getFirstCtOfClass(ccMove), escapePos)); + } + } +} + + +// ==================== updateMove ==================== + +void UnitUpdater::updateMove(Unit *unit){ + + Command *command= unit->getCurrCommand(); + const MoveCommandType *mct= static_cast(command->getCommandType()); + + Vec2i pos= command->getUnit()!=NULL? command->getUnit()->getCenteredPos(): command->getPos(); + + switch(pathFinder.findPath(unit, pos)){ + case PathFinder::tsOnTheWay: + unit->setCurrSkill(mct->getMoveSkillType()); + break; + + case PathFinder::tsBlocked: + if(unit->getPath()->isBlocked()){ + unit->finishCommand(); + } + break; + + default: + unit->finishCommand(); + } +} + + +// ==================== updateAttack ==================== + +void UnitUpdater::updateAttack(Unit *unit){ + + Command *command= unit->getCurrCommand(); + const AttackCommandType *act= static_cast(command->getCommandType()); + Unit *target= NULL; + + //if found + if(attackableOnRange(unit, &target, act->getAttackSkillType())){ + if(unit->getEp()>=act->getAttackSkillType()->getEpCost()){ + unit->setCurrSkill(act->getAttackSkillType()); + unit->setTarget(target); + } + else{ + unit->setCurrSkill(scStop); + } + } + else{ + //compute target pos + Vec2i pos; + if(command->getUnit()!=NULL){ + pos= command->getUnit()->getCenteredPos(); + } + else if(attackableOnSight(unit, &target, act->getAttackSkillType())){ + pos= target->getPos(); + } + else{ + pos= command->getPos(); + } + + //if unit arrives destPos order has ended + switch (pathFinder.findPath(unit, pos)){ + case PathFinder::tsOnTheWay: + unit->setCurrSkill(act->getMoveSkillType()); + break; + case PathFinder::tsBlocked: + if(unit->getPath()->isBlocked()){ + unit->finishCommand(); + } + break; + default: + unit->finishCommand(); + } + } +} + + +// ==================== updateAttackStopped ==================== + +void UnitUpdater::updateAttackStopped(Unit *unit){ + + Command *command= unit->getCurrCommand(); + const AttackStoppedCommandType *asct= static_cast(command->getCommandType()); + Unit *enemy; + + if(attackableOnRange(unit, &enemy, asct->getAttackSkillType())){ + unit->setCurrSkill(asct->getAttackSkillType()); + unit->setTarget(enemy); + } + else{ + unit->setCurrSkill(asct->getStopSkillType()); + } +} + + +// ==================== updateBuild ==================== + +void UnitUpdater::updateBuild(Unit *unit){ + + Command *command= unit->getCurrCommand(); + const BuildCommandType *bct= static_cast(command->getCommandType()); + + if(unit->getCurrSkill()->getClass() != scBuild) { + //if not building + const UnitType *ut= command->getUnitType(); + + switch (pathFinder.findPath(unit, command->getPos()-Vec2i(1))){ + case PathFinder::tsOnTheWay: + unit->setCurrSkill(bct->getMoveSkillType()); + break; + + case PathFinder::tsArrived: + //if arrived destination + assert(command->getUnitType()!=NULL); + if(map->isFreeCells(command->getPos(), ut->getSize(), fLand)){ + const UnitType *builtUnitType= command->getUnitType(); + + //!!! + float unitRotation = -1; + if(allowRotateUnits == true) { + char unitKey[50]=""; + sprintf(unitKey,"%d_%d",builtUnitType->getId(),unit->getFaction()->getIndex()); + unitRotation = gui->getUnitTypeBuildRotation(unitKey); + } + Unit *builtUnit= new Unit(world->getNextUnitId(), command->getPos(), builtUnitType, unit->getFaction(), world->getMap(),unitRotation); + builtUnit->create(); + + if(!builtUnitType->hasSkillClass(scBeBuilt)){ + throw runtime_error("Unit " + builtUnitType->getName() + "has no be_built skill"); + } + + builtUnit->setCurrSkill(scBeBuilt); + unit->setCurrSkill(bct->getBuildSkillType()); + unit->setTarget(builtUnit); + map->prepareTerrain(builtUnit); + command->setUnit(builtUnit); + + //play start sound + if(unit->getFactionIndex()==world->getThisFactionIndex()){ + SoundRenderer::getInstance().playFx( + bct->getStartSound(), + unit->getCurrVector(), + gameCamera->getPos()); + } + + //!!! + /* + if(unitRotation > 0) { + if(Socket::enableDebugText) printf("In [%s::%s] before sending ccRotateUnit...\n",__FILE__,__FUNCTION__); + + RotateUnitCommandType *rotateCmdType = new RotateUnitCommandType(); + rotateCmdType->setRotateAmount(unitRotation); + + Command *rotateUnitCmd = new Command(rotateCmdType); + rotateUnitCmd->setUnit(builtUnit); + + if(Socket::enableDebugText) printf("In [%s::%s] in sending ccRotateUnit...\n",__FILE__,__FUNCTION__); + + builtUnit->giveCommand(rotateUnitCmd); + + if(Socket::enableDebugText) printf("In [%s::%s] after sending ccRotateUnit...\n",__FILE__,__FUNCTION__); + } + */ + } + else{ + //if there are no free cells + unit->cancelCommand(); + unit->setCurrSkill(scStop); + if(unit->getFactionIndex()==world->getThisFactionIndex()){ + console->addStdMessage("BuildingNoPlace"); + } + } + break; + + case PathFinder::tsBlocked: + if(unit->getPath()->isBlocked()){ + unit->cancelCommand(); + } + break; + } + } + else{ + //if building + Unit *builtUnit= map->getCell(unit->getTargetPos())->getUnit(fLand); + + //if u is killed while building then u==NULL; + if(builtUnit!=NULL && builtUnit!=command->getUnit()){ + unit->setCurrSkill(scStop); + } + else if(builtUnit==NULL || builtUnit->isBuilt()){ + unit->finishCommand(); + unit->setCurrSkill(scStop); + } + else if(builtUnit->repair()){ + //building finished + unit->finishCommand(); + unit->setCurrSkill(scStop); + builtUnit->born(); + scriptManager->onUnitCreated(builtUnit); + if(unit->getFactionIndex()==world->getThisFactionIndex()){ + SoundRenderer::getInstance().playFx( + bct->getBuiltSound(), + unit->getCurrVector(), + gameCamera->getPos()); + } + } + } +} + + +// ==================== updateHarvest ==================== + +void UnitUpdater::updateHarvest(Unit *unit){ + + Command *command= unit->getCurrCommand(); + const HarvestCommandType *hct= static_cast(command->getCommandType()); + Vec2i targetPos; + + if(unit->getCurrSkill()->getClass() != scHarvest) { + //if not working + if(unit->getLoadCount()==0){ + //if not loaded go for resources + Resource *r= map->getSurfaceCell(Map::toSurfCoords(command->getPos()))->getResource(); + if(r!=NULL && hct->canHarvest(r->getType())){ + //if can harvest dest. pos + if(unit->getPos().dist(command->getPos())isResourceNear(unit->getPos(), r->getType(), targetPos)) { + //if it finds resources it starts harvesting + unit->setCurrSkill(hct->getHarvestSkillType()); + unit->setTargetPos(targetPos); + unit->setLoadCount(0); + unit->setLoadType(map->getSurfaceCell(Map::toSurfCoords(unit->getTargetPos()))->getResource()->getType()); + } + else{ + //if not continue walking + switch(pathFinder.findPath(unit, command->getPos())){ + case PathFinder::tsOnTheWay: + unit->setCurrSkill(hct->getMoveSkillType()); + break; + default: + break; + } + } + } + else{ + //if can't harvest, search for another resource + unit->setCurrSkill(scStop); + if(!searchForResource(unit, hct)){ + unit->finishCommand(); + } + } + } + else{ + //if loaded, return to store + Unit *store= world->nearestStore(unit->getPos(), unit->getFaction()->getIndex(), unit->getLoadType()); + if(store!=NULL){ + switch(pathFinder.findPath(unit, store->getCenteredPos())){ + case PathFinder::tsOnTheWay: + unit->setCurrSkill(hct->getMoveLoadedSkillType()); + break; + default: + break; + } + + //world->changePosCells(unit,unit->getPos()+unit->getDest()); + if(map->isNextTo(unit->getPos(), store)){ + + //update resources + int resourceAmount= unit->getLoadCount(); + if(unit->getFaction()->getCpuUltraControl()){ + resourceAmount*= ultraResourceFactor; + } + if(unit->getFaction()->getCpuMegaControl()){ + resourceAmount*= megaResourceFactor; + } + unit->getFaction()->incResourceAmount(unit->getLoadType(), resourceAmount); + world->getStats()->harvest(unit->getFactionIndex(), resourceAmount); + scriptManager->onResourceHarvested(); + + //if next to a store unload resources + unit->getPath()->clear(); + unit->setCurrSkill(scStop); + unit->setLoadCount(0); + } + } + else{ + unit->finishCommand(); + } + } + } + else{ + //if working + SurfaceCell *sc= map->getSurfaceCell(Map::toSurfCoords(unit->getTargetPos())); + Resource *r= sc->getResource(); + if(r!=NULL){ + //if there is a resource, continue working, until loaded + unit->update2(); + if(unit->getProgress2()>=hct->getHitsPerUnit()){ + unit->setProgress2(0); + unit->setLoadCount(unit->getLoadCount()+1); + + //if resource exausted, then delete it and stop + if(r->decAmount(1)){ + sc->deleteResource(); + unit->setCurrSkill(hct->getStopLoadedSkillType()); + } + + if(unit->getLoadCount()==hct->getMaxLoad()){ + unit->setCurrSkill(hct->getStopLoadedSkillType()); + unit->getPath()->clear(); + } + + } + } + else{ + //if there is no resource, just stop + unit->setCurrSkill(hct->getStopLoadedSkillType()); + } + } +} + + +// ==================== updateRepair ==================== + +void UnitUpdater::updateRepair(Unit *unit){ + + Command *command= unit->getCurrCommand(); + const RepairCommandType *rct= static_cast(command->getCommandType()); + + Unit *repaired= map->getCell(command->getPos())->getUnit(fLand); + bool nextToRepaired= repaired!=NULL && map->isNextTo(unit->getPos(), repaired); + + if(unit->getCurrSkill()->getClass()!=scRepair || !nextToRepaired){ + //if not repairing + if(repaired!=NULL && rct->isRepairableUnitType(repaired->getType()) && repaired->isDamaged()){ + + if(nextToRepaired){ + unit->setTarget(repaired); + unit->setCurrSkill(rct->getRepairSkillType()); + } + else{ + switch(pathFinder.findPath(unit, command->getPos())){ + case PathFinder::tsOnTheWay: + unit->setCurrSkill(rct->getMoveSkillType()); + break; + case PathFinder::tsBlocked: + if(unit->getPath()->isBlocked()){ + unit->finishCommand(); + } + break; + default: + break; + } + } + } + else{ + unit->setCurrSkill(scStop); + unit->finishCommand(); + } + } + else{ + //if repairing + if(repaired!=NULL){ + unit->setTarget(repaired); + } + if(repaired==NULL || repaired->repair()){ + unit->setCurrSkill(scStop); + unit->finishCommand(); + if(repaired!=NULL && !repaired->isBuilt()){ + repaired->born(); + scriptManager->onUnitCreated(repaired); + } + } + } +} + + +// ==================== updateProduce ==================== + +void UnitUpdater::updateProduce(Unit *unit){ + + Command *command= unit->getCurrCommand(); + const ProduceCommandType *pct= static_cast(command->getCommandType()); + Unit *produced; + + if(unit->getCurrSkill()->getClass()!=scProduce){ + //if not producing + unit->setCurrSkill(pct->getProduceSkillType()); + } + else{ + unit->update2(); + if(unit->getProgress2()>pct->getProduced()->getProductionTime()){ + unit->finishCommand(); + unit->setCurrSkill(scStop); + + //!!! + float unitRotation = -1; + if(allowRotateUnits == true) { + char unitKey[50]=""; + sprintf(unitKey,"%d_%d",pct->getId(),unit->getFaction()->getIndex()); + unitRotation = gui->getUnitTypeBuildRotation(unitKey); + } + + produced= new Unit(world->getNextUnitId(), Vec2i(0), pct->getProducedUnit(), unit->getFaction(), world->getMap(),unitRotation); + + //place unit creates the unit + if(!world->placeUnit(unit->getCenteredPos(), 10, produced)){ + delete produced; + } + else{ + produced->create(); + produced->born(); + world->getStats()->produce(unit->getFactionIndex()); + const CommandType *ct= produced->computeCommandType(unit->getMeetingPos()); + if(ct!=NULL){ + produced->giveCommand(new Command(ct, unit->getMeetingPos())); + } + scriptManager->onUnitCreated(produced); + } + } + } +} + + +// ==================== updateUpgrade ==================== + +void UnitUpdater::updateUpgrade(Unit *unit){ + + Command *command= unit->getCurrCommand(); + const UpgradeCommandType *uct= static_cast(command->getCommandType()); + + if(unit->getCurrSkill()->getClass()!=scUpgrade){ + //if not producing + unit->setCurrSkill(uct->getUpgradeSkillType()); + } + else{ + //if producing + unit->update2(); + if(unit->getProgress2()>uct->getProduced()->getProductionTime()){ + unit->finishCommand(); + unit->setCurrSkill(scStop); + unit->getFaction()->finishUpgrade(uct->getProducedUpgrade()); + } + } +} + +// ==================== updateMorph ==================== + +void UnitUpdater::updateMorph(Unit *unit){ + + Command *command= unit->getCurrCommand(); + const MorphCommandType *mct= static_cast(command->getCommandType()); + + if(unit->getCurrSkill()->getClass()!=scMorph){ + //if not morphing, check space + if(map->isFreeCellsOrHasUnit(unit->getPos(), mct->getMorphUnit()->getSize(), unit->getCurrField(), unit)){ + unit->setCurrSkill(mct->getMorphSkillType()); + } + else{ + if(unit->getFactionIndex()==world->getThisFactionIndex()){ + console->addStdMessage("InvalidPosition"); + } + unit->cancelCommand(); + } + } + else{ + unit->update2(); + if(unit->getProgress2()>mct->getProduced()->getProductionTime()){ + + //finish the command + if(unit->morph(mct)){ + unit->finishCommand(); + if(gui->isSelected(unit)){ + gui->onSelectionChanged(); + } + scriptManager->onUnitCreated(unit); + } + else{ + unit->cancelCommand(); + if(unit->getFactionIndex()==world->getThisFactionIndex()){ + console->addStdMessage("InvalidPosition"); + } + } + unit->setCurrSkill(scStop); + + } + } +} + +// ==================== PRIVATE ==================== + +// ==================== attack ==================== + +void UnitUpdater::hit(Unit *attacker){ + hit(attacker, static_cast(attacker->getCurrSkill()), attacker->getTargetPos(), attacker->getTargetField()); +} + +void UnitUpdater::hit(Unit *attacker, const AttackSkillType* ast, const Vec2i &targetPos, Field targetField){ + + //hit attack positions + if(ast->getSplash()){ + PosCircularIterator pci(map, targetPos, ast->getSplashRadius()); + while(pci.next()){ + Unit *attacked= map->getCell(pci.getPos())->getUnit(targetField); + if(attacked!=NULL){ + if(ast->getSplashDamageAll() + || !attacker->isAlly(attacked) + || ( targetPos.x==pci.getPos().x && targetPos.y==pci.getPos().y )){ + damage(attacker, ast, attacked, pci.getPos().dist(attacker->getTargetPos())); + } + } + } + } + else{ + Unit *attacked= map->getCell(targetPos)->getUnit(targetField); + if(attacked!=NULL){ + damage(attacker, ast, attacked, 0.f); + } + } + +} + +void UnitUpdater::damage(Unit *attacker, const AttackSkillType* ast, Unit *attacked, float distance){ + + //get vars + float damage= ast->getTotalAttackStrength(attacker->getTotalUpgrade()); + int var= ast->getAttackVar(); + int armor= attacked->getType()->getTotalArmor(attacked->getTotalUpgrade()); + float damageMultiplier= world->getTechTree()->getDamageMultiplier(ast->getAttackType(), attacked->getType()->getArmorType()); + + //compute damage + damage+= random.randRange(-var, var); + damage/= distance+1; + damage-= armor; + damage*= damageMultiplier; + if(damage<1){ + damage= 1; + } + + //damage the unit + if(attacked->decHp(static_cast(damage))){ + world->getStats()->kill(attacker->getFactionIndex(), attacked->getFactionIndex()); + attacker->incKills(); + scriptManager->onUnitDied(attacked); + } +} + +void UnitUpdater::startAttackParticleSystem(Unit *unit){ + Renderer &renderer= Renderer::getInstance(); + + ProjectileParticleSystem *psProj = 0; + SplashParticleSystem *psSplash; + + const AttackSkillType *ast= static_cast(unit->getCurrSkill()); + ParticleSystemTypeProjectile *pstProj= ast->getProjParticleType(); + ParticleSystemTypeSplash *pstSplash= ast->getSplashParticleType(); + + Vec3f startPos= unit->getCurrVector(); + Vec3f endPos= unit->getTargetVec(); + + //make particle system + const SurfaceCell *sc= map->getSurfaceCell(Map::toSurfCoords(unit->getPos())); + const SurfaceCell *tsc= map->getSurfaceCell(Map::toSurfCoords(unit->getTargetPos())); + bool visible= sc->isVisible(world->getThisTeamIndex()) || tsc->isVisible(world->getThisTeamIndex()); + + //projectile + if(pstProj!=NULL){ + psProj= pstProj->create(); + psProj->setPath(startPos, endPos); + psProj->setObserver(new ParticleDamager(unit, this, gameCamera)); + psProj->setVisible(visible); + psProj->setFactionColor(unit->getFaction()->getTexture()->getPixmap()->getPixel3f(0,0)); + renderer.manageParticleSystem(psProj, rsGame); + } + else{ + hit(unit); + } + + //splash + if(pstSplash!=NULL){ + psSplash= pstSplash->create(); + psSplash->setPos(endPos); + psSplash->setVisible(visible); + psSplash->setFactionColor(unit->getFaction()->getTexture()->getPixmap()->getPixel3f(0,0)); + renderer.manageParticleSystem(psSplash, rsGame); + if(pstProj!=NULL){ + psProj->link(psSplash); + } + } +} + +// ==================== misc ==================== + +//looks for a resource of type rt, if rt==NULL looks for any +//resource the unit can harvest +bool UnitUpdater::searchForResource(Unit *unit, const HarvestCommandType *hct){ + + Vec2i pos= unit->getCurrCommand()->getPos(); + + for(int radius= 0; radiusisInside(i, j)){ + Resource *r= map->getSurfaceCell(Map::toSurfCoords(Vec2i(i, j)))->getResource(); + if(r!=NULL){ + if(hct->canHarvest(r->getType())){ + unit->getCurrCommand()->setPos(Vec2i(i, j)); + return true; + } + } + } + } + } + } + return false; +} + +bool UnitUpdater::attackerOnSight(const Unit *unit, Unit **rangedPtr){ + int range= unit->getType()->getSight(); + return unitOnRange(unit, range, rangedPtr, NULL); +} + +bool UnitUpdater::attackableOnSight(const Unit *unit, Unit **rangedPtr, const AttackSkillType *ast){ + int range= unit->getType()->getSight(); + return unitOnRange(unit, range, rangedPtr, ast); +} + +bool UnitUpdater::attackableOnRange(const Unit *unit, Unit **rangedPtr, const AttackSkillType *ast){ + int range= ast->getTotalAttackRange(unit->getTotalUpgrade()); + return unitOnRange(unit, range, rangedPtr, ast); +} + +//if the unit has any enemy on range +bool UnitUpdater::unitOnRange(const Unit *unit, int range, Unit **rangedPtr, const AttackSkillType *ast){ + + vector enemies; + + //we check command target + const Unit *commandTarget= NULL; + if(unit->anyCommand()){ + commandTarget= static_cast(unit->getCurrCommand()->getUnit()); + } + if(commandTarget!=NULL && commandTarget->isDead()){ + commandTarget= NULL; + } + + //aux vars + int size= unit->getType()->getSize(); + Vec2i center= unit->getPos(); + Vec2f floatCenter= unit->getFloatCenteredPos(); + + //nearby cells + for(int i=center.x-range; iisInside(i, j) && floor(floatCenter.dist(Vec2f(i, j))) <= (range+1)){ + + //all fields + for(int k=0; k(k); + + //check field + if((ast==NULL || ast->getAttackField(f))){ + Unit *possibleEnemy= map->getCell(i, j)->getUnit(f); + + //check enemy + if(possibleEnemy!=NULL && possibleEnemy->isAlive()){ + if((!unit->isAlly(possibleEnemy) && commandTarget==NULL) || commandTarget==possibleEnemy){ + enemies.push_back(possibleEnemy); + } + } + } + } + } + } + } + + //attack enemies that can attack first + for(int i=0; igetType()->hasSkillClass(scAttack)){ + *rangedPtr= enemies[i]; + return true; + } + } + + //any enemy + if(enemies.size()>0){ + *rangedPtr= enemies.front(); + return true; + } + + return false; +} + +// ===================================================== +// class ParticleDamager +// ===================================================== + +ParticleDamager::ParticleDamager(Unit *attacker, UnitUpdater *unitUpdater, const GameCamera *gameCamera){ + this->gameCamera= gameCamera; + this->attackerRef= attacker; + this->ast= static_cast(attacker->getCurrSkill()); + this->targetPos= attacker->getTargetPos(); + this->targetField= attacker->getTargetField(); + this->unitUpdater= unitUpdater; +} + +void ParticleDamager::update(ParticleSystem *particleSystem){ + Unit *attacker= attackerRef.getUnit(); + + if(attacker!=NULL){ + + unitUpdater->hit(attacker, ast, targetPos, targetField); + + //play sound + StaticSound *projSound= ast->getProjSound(); + if(particleSystem->getVisible() && projSound!=NULL){ + SoundRenderer::getInstance().playFx(projSound, attacker->getCurrVector(), gameCamera->getPos()); + } + } + particleSystem->setObserver(NULL); + delete this; +} + +}}//end namespace diff --git a/source/glest_game/world/unit_updater.h b/source/glest_game/world/unit_updater.h new file mode 100644 index 00000000..ed231e00 --- /dev/null +++ b/source/glest_game/world/unit_updater.h @@ -0,0 +1,115 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_UNITUPDATER_H_ +#define _GLEST_GAME_UNITUPDATER_H_ + +#include "gui.h" +#include "path_finder.h" +#include "particle.h" +#include "random.h" + +using Shared::Graphics::ParticleObserver; +using Shared::Util::Random; + +namespace Glest{ namespace Game{ + +class Unit; +class Map; +class ScriptManager; + +// ===================================================== +// class UnitUpdater +// +/// Updates all units in the game, even the player +/// controlled units, performs basic actions only +/// such as responding to an attack +// ===================================================== + +class ParticleDamager; + +class UnitUpdater{ +private: + friend class ParticleDamager; + +private: + static const int maxResSearchRadius= 10; + static const int harvestDistance= 5; + static const int ultraResourceFactor= 3; + static const int megaResourceFactor= 4; + +private: + const GameCamera *gameCamera; + Gui *gui; + Map *map; + World *world; + Console *console; + ScriptManager *scriptManager; + PathFinder pathFinder; + Random random; + bool allowRotateUnits; + +public: + void init(Game *game); + + //update skills + void updateUnit(Unit *unit); + + //update commands + void updateUnitCommand(Unit *unit); + void updateStop(Unit *unit); + void updateMove(Unit *unit); + void updateAttack(Unit *unit); + void updateAttackStopped(Unit *unit); + void updateBuild(Unit *unit); + void updateHarvest(Unit *unit); + void updateRepair(Unit *unit); + void updateProduce(Unit *unit); + void updateUpgrade(Unit *unit); + void updateMorph(Unit *unit); + +private: + //attack + void hit(Unit *attacker); + void hit(Unit *attacker, const AttackSkillType* ast, const Vec2i &targetPos, Field targetField); + void damage(Unit *attacker, const AttackSkillType* ast, Unit *attacked, float distance); + void startAttackParticleSystem(Unit *unit); + + //misc + bool searchForResource(Unit *unit, const HarvestCommandType *hct); + bool attackerOnSight(const Unit *unit, Unit **enemyPtr); + bool attackableOnSight(const Unit *unit, Unit **enemyPtr, const AttackSkillType *ast); + bool attackableOnRange(const Unit *unit, Unit **enemyPtr, const AttackSkillType *ast); + bool unitOnRange(const Unit *unit, int range, Unit **enemyPtr, const AttackSkillType *ast); + void enemiesAtDistance(const Unit *unit, const Unit *priorityUnit, int distance, vector &enemies); +}; + +// ===================================================== +// class ParticleDamager +// ===================================================== + +class ParticleDamager: public ParticleObserver{ +public: + UnitReference attackerRef; + const AttackSkillType* ast; + UnitUpdater *unitUpdater; + const GameCamera *gameCamera; + Vec2i targetPos; + Field targetField; + +public: + ParticleDamager(Unit *attacker, UnitUpdater *unitUpdater, const GameCamera *gameCamera); + virtual void update(ParticleSystem *particleSystem); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/world/world.cpp b/source/glest_game/world/world.cpp index cae30787..d79cf19e 100644 --- a/source/glest_game/world/world.cpp +++ b/source/glest_game/world/world.cpp @@ -48,6 +48,8 @@ World::World(){ scriptManager= NULL; this->game = NULL; + + allowRotateUnits = Config::getInstance().getBool("AllowRotateUnits","0"); } void World::end(){ @@ -312,9 +314,12 @@ void World::createUnit(const string &unitName, int factionIndex, const Vec2i &po const UnitType* ut= ft->getUnitType(unitName); //!!! - //if(Socket::enableDebugText) printf("In [%s::%s] A\n",__FILE__,__FUNCTION__); - float unitRotation = game->getGui()->getUnitTypeBuildRotation(ut->getId()); - //if(Socket::enableDebugText) printf("In [%s::%s] B\n",__FILE__,__FUNCTION__); + float unitRotation = -1; + if(allowRotateUnits == true) { + char unitKey[50]=""; + sprintf(unitKey,"%d_%d",ut->getId(),faction->getIndex()); + unitRotation = game->getGui()->getUnitTypeBuildRotation(unitKey); + } Unit* unit= new Unit(getNextUnitId(), pos, ut, faction, &map, unitRotation); @@ -575,9 +580,12 @@ void World::initUnits(){ for(int l=0; lgetGui()->getUnitTypeBuildRotation(ut->getId()); - //if(Socket::enableDebugText) printf("In [%s::%s] B\n",__FILE__,__FUNCTION__); + float unitRotation = -1; + if(allowRotateUnits == true) { + char unitKey[50]=""; + sprintf(unitKey,"%d_%d",ut->getId(),f->getIndex()); + unitRotation = game->getGui()->getUnitTypeBuildRotation(unitKey); + } Unit *unit= new Unit(getNextUnitId(), Vec2i(0), ut, f, &map, unitRotation); diff --git a/source/glest_game/world/world.h b/source/glest_game/world/world.h new file mode 100644 index 00000000..43374b64 --- /dev/null +++ b/source/glest_game/world/world.h @@ -0,0 +1,167 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_WORLD_H_ +#define _GLEST_GAME_WORLD_H_ + +#include "vec.h" +#include "math_util.h" +#include "resource.h" +#include "tech_tree.h" +#include "tileset.h" +#include "console.h" +#include "map.h" +#include "scenario.h" +#include "minimap.h" +#include "logger.h" +#include "stats.h" +#include "time_flow.h" +#include "upgrade.h" +#include "water_effects.h" +#include "faction.h" +#include "unit_updater.h" +#include "random.h" +#include "game_constants.h" + +namespace Glest{ namespace Game{ + +using Shared::Graphics::Quad2i; +using Shared::Graphics::Rect2i; +using Shared::Util::Random; + +class Faction; +class Unit; +class Config; +class Game; +class GameSettings; +class ScriptManager; + +// ===================================================== +// class World +// +/// The game world: Map + Tileset + TechTree +// ===================================================== + +class World{ +private: + typedef vector Factions; + +public: + static const int generationArea= 100; + static const float airHeight; + static const int indirectSightRange= 5; + +private: + + Map map; + Tileset tileset; + TechTree techTree; + TimeFlow timeFlow; + Scenario scenario; + + UnitUpdater unitUpdater; + WaterEffects waterEffects; + Minimap minimap; + Stats stats; //BattleEnd will delete this object + + Factions factions; + + Random random; + + ScriptManager* scriptManager; + + int thisFactionIndex; + int thisTeamIndex; + int frameCount; + int nextUnitId; + + //config + bool fogOfWar; + int fogOfWarSmoothingFrameSkip; + bool fogOfWarSmoothing; + Game *game; + bool allowRotateUnits; + +public: + World(); + void end(); //to die before selection does + + //get + int getMaxPlayers() const {return map.getMaxPlayers();} + int getThisFactionIndex() const {return thisFactionIndex;} + int getThisTeamIndex() const {return thisTeamIndex;} + const Faction *getThisFaction() const {return &factions[thisFactionIndex];} + int getFactionCount() const {return factions.size();} + const Map *getMap() const {return ↦} + const Tileset *getTileset() const {return &tileset;} + const TechTree *getTechTree() const {return &techTree;} + const Scenario *getScenario() const {return &scenario;} + const TimeFlow *getTimeFlow() const {return &timeFlow;} + Tileset *getTileset() {return &tileset;} + Map *getMap() {return ↦} + const Faction *getFaction(int i) const {return &factions[i];} + Faction *getFaction(int i) {return &factions[i];} + const Minimap *getMinimap() const {return &minimap;} + const Stats *getStats() const {return &stats;}; + Stats *getStats() {return &stats;}; + const WaterEffects *getWaterEffects() const {return &waterEffects;} + int getNextUnitId() {return nextUnitId++;} + int getFrameCount() const {return frameCount;} + + //init & load + void init(Game *game, bool createUnits); + void loadTileset(const string &dir, Checksum* checksum); + void loadTech(const string &dir, set &factions, Checksum* checksum); + void loadMap(const string &path, Checksum* checksum); + void loadScenario(const string &path, Checksum* checksum); + + //misc + void update(); + Unit* findUnitById(int id); + const UnitType* findUnitTypeById(const FactionType* factionType, int id); + bool placeUnit(const Vec2i &startLoc, int radius, Unit *unit, bool spaciated= false); + void moveUnitCells(Unit *unit); + bool toRenderUnit(const Unit *unit, const Quad2i &visibleQuad) const; + bool toRenderUnit(const Unit *unit) const; + Unit *nearestStore(const Vec2i &pos, int factionIndex, const ResourceType *rt); + + //scripting interface + void createUnit(const string &unitName, int factionIndex, const Vec2i &pos); + void givePositionCommand(int unitId, const string &commandName, const Vec2i &pos); + void giveProductionCommand(int unitId, const string &producedName); + void giveUpgradeCommand(int unitId, const string &upgradeName); + void giveResource(const string &resourceName, int factionIndex, int amount); + int getResourceAmount(const string &resourceName, int factionIndex); + Vec2i getStartLocation(int factionIndex); + Vec2i getUnitPosition(int unitId); + int getUnitFactionIndex(int unitId); + int getUnitCount(int factionIndex); + int getUnitCountOfType(int factionIndex, const string &typeName); + +private: + + void initCells(); + void initSplattedTextures(); + void initFactionTypes(GameSettings *gs); + void initMinimap(); + void initUnits(); + void initMap(); + void initExplorationState(); + + //misc + void tick(); + void computeFow(); + void exploreCells(const Vec2i &newPos, int sightRange, int teamIndex); +}; + +}}//end namespace + +#endif diff --git a/source/glest_map_editor/main.cpp b/source/glest_map_editor/main.cpp new file mode 100755 index 00000000..7f85cb23 --- /dev/null +++ b/source/glest_map_editor/main.cpp @@ -0,0 +1,886 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Marti�o Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + + + +#include "main.h" + +#include + +#include "conversion.h" +#include + +using namespace Shared::Util; +using namespace std; + +namespace MapEditor { + +const string MainWindow::versionString = "v1.5.0-beta3"; +const string MainWindow::winHeader = "Glest Map Editor " + versionString + " - Built: " + __DATE__; + +// =============================================== +// class Global functions +// =============================================== + +wxString ToUnicode(const char* str) { + return wxString(str, wxConvUTF8); +} + +wxString ToUnicode(const string& str) { + return wxString(str.c_str(), wxConvUTF8); +} + +// =============================================== +// class MainWindow +// =============================================== + +MainWindow::MainWindow() + : wxFrame(NULL, -1, ToUnicode(winHeader), wxDefaultPosition, wxSize(800, 600)) { + + fileModified=false; + lastX=0; + lastY=0; + radius=1; + height=0; + surface=1; + object=0; + resource=0; + startLocation=1; + enabledGroup=ctHeight; + currentBrush=btHeight; + resourceUnderMouse=0; + objectUnderMouse=0; + + //gl canvas + int args[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER }; + glCanvas = new GlCanvas(this, args); + + //menus + menuBar = new wxMenuBar(); + + //file + menuFile = new wxMenu(); + menuFile->Append(wxID_OPEN); + menuFile->AppendSeparator(); + menuFile->Append(wxID_SAVE); + menuFile->Append(wxID_SAVEAS); + menuFile->AppendSeparator(); + menuFile->Append(wxID_EXIT); + menuBar->Append(menuFile, wxT("&File")); + + //edit + menuEdit = new wxMenu(); + menuEdit->Append(miEditUndo, wxT("&Undo\tCTRL+z")); + menuEdit->Append(miEditRedo, wxT("&Redo\tCTRL+y")); + menuEdit->Append(miEditReset, wxT("Rese&t")); + menuEdit->Append(miEditResetPlayers, wxT("Reset &Players")); + menuEdit->Append(miEditResize, wxT("Re&size")); + menuEdit->Append(miEditFlipX, wxT("Flip &X")); + menuEdit->Append(miEditFlipY, wxT("Flip &Y")); + menuEdit->Append(miEditRandomizeHeights, wxT("Randomize &Heights")); + menuEdit->Append(miEditRandomize, wxT("Randomi&ze")); + menuEdit->Append(miEditSwitchSurfaces, wxT("Switch Su&rfaces")); + menuEdit->Append(miEditInfo, wxT("&Info")); + menuEdit->Append(miEditAdvanced, wxT("&Advanced")); + menuBar->Append(menuEdit, wxT("&Edit")); + + //misc + menuMisc = new wxMenu(); + menuMisc->Append(miMiscResetZoomAndPos, wxT("&Reset zoom and pos")); + menuMisc->Append(miMiscAbout, wxT("&About")); + menuMisc->Append(miMiscHelp, wxT("&Help")); + menuBar->Append(menuMisc, wxT("&Misc")); + + //brush + menuBrush = new wxMenu(); + + // Glest height brush + menuBrushHeight = new wxMenu(); + for (int i = 0; i < heightCount; ++i) { + menuBrushHeight->AppendCheckItem(miBrushHeight + i + 1, ToUnicode(intToStr(i - heightCount / 2))); + } + menuBrushHeight->Check(miBrushHeight + (heightCount + 1) / 2, true); + menuBrush->Append(miBrushHeight, wxT("&Height"), menuBrushHeight); + + enabledGroup = ctHeight; + + // ZombiePirate height brush + menuBrushGradient = new wxMenu(); + for (int i = 0; i < heightCount; ++i) { + menuBrushGradient->AppendCheckItem(miBrushGradient + i + 1, ToUnicode(intToStr(i - heightCount / 2))); + } + menuBrush->Append(miBrushGradient, wxT("&Gradient"), menuBrushGradient); + + //surface + menuBrushSurface = new wxMenu(); + menuBrushSurface->AppendCheckItem(miBrushSurface + 1, wxT("&1 - Grass")); + menuBrushSurface->AppendCheckItem(miBrushSurface + 2, wxT("&2 - Secondary Grass")); + menuBrushSurface->AppendCheckItem(miBrushSurface + 3, wxT("&3 - Road")); + menuBrushSurface->AppendCheckItem(miBrushSurface + 4, wxT("&4 - Stone")); + menuBrushSurface->AppendCheckItem(miBrushSurface + 5, wxT("&5 - Ground")); + menuBrush->Append(miBrushSurface, wxT("&Surface"), menuBrushSurface); + + //objects + menuBrushObject = new wxMenu(); + menuBrushObject->AppendCheckItem(miBrushObject + 1, wxT("&0 - None (erase)")); + menuBrushObject->AppendCheckItem(miBrushObject+2, wxT("&1 - Tree (unwalkable/harvestable)")); + menuBrushObject->AppendCheckItem(miBrushObject+3, wxT("&2 - DeadTree/Cactuses/Thornbush (unwalkable)")); + menuBrushObject->AppendCheckItem(miBrushObject+4, wxT("&3 - Stone (unwalkable)")); + menuBrushObject->AppendCheckItem(miBrushObject+5, wxT("&4 - Bush/Grass/Fern (walkable)")); + menuBrushObject->AppendCheckItem(miBrushObject+6, wxT("&5 - Water Object/Reed/Papyrus (walkable)")); + menuBrushObject->AppendCheckItem(miBrushObject+7, wxT("&6 - C1 BigTree/DeadTree/OldPalm (unwalkable/not harvestable)")); + menuBrushObject->AppendCheckItem(miBrushObject+8, wxT("&7 - C2 Hanged/Impaled (unwalkable)")); + menuBrushObject->AppendCheckItem(miBrushObject+9, wxT("&8 - C3 Statues (unwalkable)")); + menuBrushObject->AppendCheckItem(miBrushObject+10, wxT("&9 - C4 Big Rock (Mountain) (unwalkable)")); + menuBrushObject->AppendCheckItem(miBrushObject+11, wxT("10 &- C5 Invisible Blocking Object (unwalkable)")); + menuBrush->Append(miBrushObject, wxT("&Object"), menuBrushObject); + + //resources + menuBrushResource = new wxMenu(); + menuBrushResource->AppendCheckItem(miBrushResource + 1, wxT("&0 - None")); + menuBrushResource->AppendCheckItem(miBrushResource+2, wxT("&1 - gold (unwalkable)")); + menuBrushResource->AppendCheckItem(miBrushResource+3, wxT("&2 - stone (unwalkable)")); + menuBrushResource->AppendCheckItem(miBrushResource+4, wxT("&3 - custom")); + menuBrushResource->AppendCheckItem(miBrushResource+5, wxT("&4 - custom")); + menuBrushResource->AppendCheckItem(miBrushResource+6, wxT("&5 - custom")); + menuBrush->Append(miBrushResource, wxT("&Resource"), menuBrushResource); + + //players + menuBrushStartLocation = new wxMenu(); + menuBrushStartLocation->AppendCheckItem(miBrushStartLocation + 1, wxT("&1 - Player 1")); + menuBrushStartLocation->AppendCheckItem(miBrushStartLocation + 2, wxT("&2 - Player 2")); + menuBrushStartLocation->AppendCheckItem(miBrushStartLocation + 3, wxT("&3 - Player 3")); + menuBrushStartLocation->AppendCheckItem(miBrushStartLocation + 4, wxT("&4 - Player 4")); + menuBrushStartLocation->AppendCheckItem(miBrushStartLocation + 5, wxT("&5 - Player 5 ")); + menuBrushStartLocation->AppendCheckItem(miBrushStartLocation + 6, wxT("&6 - Player 6 ")); + menuBrushStartLocation->AppendCheckItem(miBrushStartLocation + 7, wxT("&7 - Player 7 ")); + menuBrushStartLocation->AppendCheckItem(miBrushStartLocation + 8, wxT("&8 - Player 8 ")); + menuBrush->Append(miBrushStartLocation, wxT("&Player"), menuBrushStartLocation); + menuBar->Append(menuBrush, wxT("&Brush")); + + //radius + menuRadius = new wxMenu(); + for (int i = 1; i <= radiusCount; ++i) { + menuRadius->AppendCheckItem(miRadius + i, ToUnicode("&" + intToStr(i) + "\tALT+" + intToStr(i))); + } + menuRadius->Check(miRadius + 1, true); + menuBar->Append(menuRadius, wxT("&Radius")); + + SetMenuBar(menuBar); + + fileName = "New (unsaved) map"; + int status_widths[siCOUNT] = { + 10, // empty + -2, // File name + -1, // File type + -2, // Current Object + -2, // Brush Type + -2, // Brush 'Value' + -1, // Brush Radius + }; + CreateStatusBar(siCOUNT); + GetStatusBar()->SetStatusWidths(siCOUNT, status_widths); + + SetStatusText(wxT("File: ") + ToUnicode(fileName), siFILE_NAME); + SetStatusText(wxT(".gbm"), siFILE_TYPE); + SetStatusText(wxT("Object: None (Erase)"), siCURR_OBJECT); + SetStatusText(wxT("Brush: Height"), siBRUSH_TYPE); + SetStatusText(wxT("Value: 0"), siBRUSH_VALUE); + SetStatusText(wxT("Radius: 1"), siBRUSH_RADIUS); + +#ifndef WIN32 + timer = new wxTimer(this); + timer->Start(100); +#endif + glCanvas->SetFocus(); +} + +void MainWindow::init(string fname) { + glCanvas->SetCurrent(); + program = new Program(GetClientSize().x, GetClientSize().y); + + fileName = "New (unsaved) Map"; + if (!fname.empty() && fileExists(fname)) { + program->loadMap(fname); + currentFile = fname; + fileName = cutLastExt(lastFile(fname)); + } + SetTitle(ToUnicode(winHeader + "; " + currentFile)); + setDirty(false); + setExtension(); +} + +void MainWindow::onClose(wxCloseEvent &event) { + delete this; +} + +MainWindow::~MainWindow() { + delete program; + delete glCanvas; +} + +void MainWindow::setDirty(bool val) { + if (fileModified && val) { + return; + } + fileModified = val; + if (fileModified) { + SetStatusText(wxT("File: ") + ToUnicode(fileName) + wxT("*"), siFILE_NAME); + } else { + SetStatusText(wxT("File: ") + ToUnicode(fileName), siFILE_NAME); + } +} + +void MainWindow::setExtension() { + if (currentFile.empty()) { + return; + } + string extnsn = ext(currentFile); + if (extnsn == "gbm" || extnsn == "mgm") { + currentFile = cutLastExt(currentFile); + } + if (Program::getMap()->getMaxFactions() <= 4) { + SetStatusText(wxT(".gbm"), siFILE_TYPE); + currentFile += ".gbm"; + } else { + SetStatusText(wxT(".mgm"), siFILE_TYPE); + currentFile += ".mgm"; + } +} + +void MainWindow::onTimer(wxTimerEvent &event) { + wxPaintEvent paintEvent; + onPaint(paintEvent); + +} + +void MainWindow::onMouseDown(wxMouseEvent &event) { + if (event.LeftIsDown()) { + program->setUndoPoint(enabledGroup); + program->setRefAlt(event.GetX(), event.GetY()); + change(event.GetX(), event.GetY()); + if (!isDirty()) { + setDirty(true); + } + wxPaintEvent ev; + onPaint(ev); + } + event.Skip(); +} + +void MainWindow::onMouseMove(wxMouseEvent &event) { + int dif; + + int x = event.GetX(); + int y = event.GetY(); + + bool doPaint = true; + if (event.LeftIsDown()) { + change(x, y); + } else if (event.MiddleIsDown()) { + dif = (y - lastY); + if (dif != 0) { + program->incCellSize(dif / abs(dif)); + } + } else if (event.RightIsDown()) { + program->setOfset(x - lastX, y - lastY); + } else { + doPaint = false; + } + lastX = x; + lastY = y; + if (doPaint) { + wxPaintEvent ev; + onPaint(ev); + } + else { + int currResource = program->getResource(x,y); + if(currResource>0){ + SetStatusText(wxT("Resource: ") + ToUnicode(resource_descs[currResource]), siCURR_OBJECT); + resourceUnderMouse = currResource; + objectUnderMouse = 0; + } + else { + int currObject = program->getObject(x,y); + SetStatusText(wxT("Object: ") + ToUnicode(object_descs[currObject]), siCURR_OBJECT); + resourceUnderMouse = 0; + objectUnderMouse = currObject; + } + } + + event.Skip(); +} + +void MainWindow::onPaint(wxPaintEvent &event) { + program->renderMap(GetClientSize().x, GetClientSize().y); + glCanvas->SwapBuffers(); +} + +void MainWindow::onMenuFileLoad(wxCommandEvent &event) { + wxFileDialog fileDialog(this); + fileDialog.SetWildcard(wxT("Glest Map (*.gbm)|*.gbm|Mega Map (*.mgm)|*.mgm")); + if (fileDialog.ShowModal() == wxID_OK) { + currentFile = fileDialog.GetPath().ToAscii(); + program->loadMap(currentFile); + fileName = cutLastExt(lastFile(currentFile)); + setDirty(false); + setExtension(); + SetTitle(ToUnicode(winHeader + "; " + currentFile)); + } +} + +void MainWindow::onMenuFileSave(wxCommandEvent &event) { + if (currentFile.empty()) { + wxCommandEvent ev; + onMenuFileSaveAs(ev); + } else { + setExtension(); + program->saveMap(currentFile); + setDirty(false); + } +} + +void MainWindow::onMenuFileSaveAs(wxCommandEvent &event) { + wxFileDialog fileDialog(this, wxT("Select file"), wxT(""), wxT(""), wxT("*.gbm|*.mgm"), wxSAVE); + fileDialog.SetWildcard(wxT("Glest Map (*.gbm)|*.gbm|Mega Map (*.mgm)|*.mgm")); + if (fileDialog.ShowModal() == wxID_OK) { + currentFile = fileDialog.GetPath().ToAscii(); + setExtension(); + program->saveMap(currentFile); + fileName = cutLastExt(lastFile(currentFile)); + setDirty(false); + } + SetTitle(ToUnicode(winHeader + "; " + currentFile)); +} + +void MainWindow::onMenuFileExit(wxCommandEvent &event) { + Close(); +} + +void MainWindow::onMenuEditUndo(wxCommandEvent &event) { + std::cout << "Undo Pressed" << std::endl; + if (program->undo()) { + Refresh(); + setDirty(); + } +} + +void MainWindow::onMenuEditRedo(wxCommandEvent &event) { + std::cout << "Redo Pressed" << std::endl; + if (program->redo()) { + Refresh(); + setDirty(); + } +} + +void MainWindow::onMenuEditReset(wxCommandEvent &event) { + program->setUndoPoint(ctAll); + SimpleDialog simpleDialog; + simpleDialog.addValue("Altitude", "10"); + simpleDialog.addValue("Surface", "1"); + simpleDialog.addValue("Width", "64"); + simpleDialog.addValue("Height", "64"); + simpleDialog.show(); + + try { + program->reset( + strToInt(simpleDialog.getValue("Width")), + strToInt(simpleDialog.getValue("Height")), + strToInt(simpleDialog.getValue("Altitude")), + strToInt(simpleDialog.getValue("Surface"))); + } catch (const exception &e) { + wxMessageDialog(NULL, ToUnicode(e.what()), wxT("Exception"), wxOK | wxICON_ERROR).ShowModal(); + } + currentFile = ""; + fileName = "New (unsaved) map"; +} + +void MainWindow::onMenuEditResetPlayers(wxCommandEvent &event) { + SimpleDialog simpleDialog; + simpleDialog.addValue("Factions", intToStr(program->getMap()->getMaxFactions())); + simpleDialog.show(); + + try { + program->resetFactions(strToInt(simpleDialog.getValue("Factions"))); + } catch (const exception &e) { + wxMessageDialog(NULL, ToUnicode(e.what()), wxT("Exception"), wxOK | wxICON_ERROR).ShowModal(); + } + setDirty(); + setExtension(); +} + +void MainWindow::onMenuEditResize(wxCommandEvent &event) { + SimpleDialog simpleDialog; + simpleDialog.addValue("Altitude", "10"); + simpleDialog.addValue("Surface", "1"); + simpleDialog.addValue("Height", "64"); + simpleDialog.addValue("Width", "64"); + simpleDialog.show(); + + try { + program->resize( + strToInt(simpleDialog.getValue("Height")), + strToInt(simpleDialog.getValue("Width")), + strToInt(simpleDialog.getValue("Altitude")), + strToInt(simpleDialog.getValue("Surface"))); + } catch (const exception &e) { + wxMessageDialog(NULL, ToUnicode(e.what()), wxT("Exception"), wxOK | wxICON_ERROR).ShowModal(); + } + setDirty(); +} + +void MainWindow::onMenuEditFlipX(wxCommandEvent &event) { + program->flipX(); + setDirty(); +} + +void MainWindow::onMenuEditFlipY(wxCommandEvent &event) { + program->flipY(); + setDirty(); +} + +void MainWindow::onMenuEditRandomizeHeights(wxCommandEvent &event) { + program->randomizeMapHeights(); + setDirty(); +} + +void MainWindow::onMenuEditRandomize(wxCommandEvent &event) { + program->randomizeMap(); + setDirty(); +} + +void MainWindow::onMenuEditSwitchSurfaces(wxCommandEvent &event) { + SimpleDialog simpleDialog; + simpleDialog.addValue("Surface1", "1"); + simpleDialog.addValue("Surface2", "2"); + simpleDialog.show(); + + try { + program->switchMapSurfaces( + strToInt(simpleDialog.getValue("Surface1")), + strToInt(simpleDialog.getValue("Surface2"))); + } catch (const exception &e) { + wxMessageDialog(NULL, ToUnicode(e.what()), wxT("Exception"), wxOK | wxICON_ERROR).ShowModal(); + } + setDirty(); +} + +void MainWindow::onMenuEditInfo(wxCommandEvent &event) { + SimpleDialog simpleDialog; + simpleDialog.addValue("Title", program->getMap()->getTitle()); + simpleDialog.addValue("Desc", program->getMap()->getDesc()); + simpleDialog.addValue("Author", program->getMap()->getAuthor()); + + simpleDialog.show(); + + if (program->setMapTitle(simpleDialog.getValue("Title")) + || program->setMapDesc(simpleDialog.getValue("Desc")) + || program->setMapAuthor(simpleDialog.getValue("Author"))) { + if (!isDirty()) { + setDirty(true); + } + } +} + +void MainWindow::onMenuEditAdvanced(wxCommandEvent &event) { + SimpleDialog simpleDialog; + simpleDialog.addValue("Height Factor", intToStr(program->getMap()->getHeightFactor())); + simpleDialog.addValue("Water Level", intToStr(program->getMap()->getWaterLevel())); + + simpleDialog.show(); + + try { + program->setMapAdvanced( + strToInt(simpleDialog.getValue("Height Factor")), + strToInt(simpleDialog.getValue("Water Level"))); + } catch (const exception &e) { + wxMessageDialog(NULL, ToUnicode(e.what()), wxT("Exception"), wxOK | wxICON_ERROR).ShowModal(); + } + setDirty(); +} + +void MainWindow::onMenuMiscResetZoomAndPos(wxCommandEvent &event) { + program->resetOfset(); +} + +void MainWindow::onMenuMiscAbout(wxCommandEvent &event) { + wxMessageDialog( + NULL, + wxT("Glest Map Editor - Copyright 2004 The Glest Team\n(with improvements by others, 2010)."), + wxT("About")).ShowModal(); +} + +void MainWindow::onMenuMiscHelp(wxCommandEvent &event) { + wxMessageDialog( + NULL, + wxT("Left mouse click: draw\nRight mouse drag: move\nCenter mouse drag: zoom"), + wxT("Help")).ShowModal(); +} + +void MainWindow::onMenuBrushHeight(wxCommandEvent &e) { + uncheckBrush(); + menuBrushHeight->Check(e.GetId(), true); + height = e.GetId() - miBrushHeight - heightCount / 2 - 1; + enabledGroup = ctHeight; + currentBrush = btHeight; + SetStatusText(wxT("Brush: Height"), siBRUSH_TYPE); + SetStatusText(wxT("Value: ") + ToUnicode(intToStr(height)), siBRUSH_VALUE); +} + +void MainWindow::onMenuBrushGradient(wxCommandEvent &e) { + uncheckBrush(); + menuBrushGradient->Check(e.GetId(), true); + height = e.GetId() - miBrushGradient - heightCount / 2 - 1; + enabledGroup = ctGradient; + currentBrush = btGradient; + SetStatusText(wxT("Brush: Gradient"), siBRUSH_TYPE); + SetStatusText(wxT("Value: ") + ToUnicode(intToStr(height)), siBRUSH_VALUE); +} + + +void MainWindow::onMenuBrushSurface(wxCommandEvent &e) { + uncheckBrush(); + menuBrushSurface->Check(e.GetId(), true); + surface = e.GetId() - miBrushSurface; + enabledGroup = ctSurface; + currentBrush = btSurface; + SetStatusText(wxT("Brush: Surface"), siBRUSH_TYPE); + SetStatusText( + wxT("Value: ") + ToUnicode(intToStr(surface)) + wxT(" ") + + ToUnicode(surface_descs[surface - 1]), siBRUSH_VALUE); +} + +void MainWindow::onMenuBrushObject(wxCommandEvent &e) { + uncheckBrush(); + menuBrushObject->Check(e.GetId(), true); + object = e.GetId() - miBrushObject - 1; + enabledGroup = ctObject; + currentBrush = btObject; + SetStatusText(wxT("Brush: Object"), siBRUSH_TYPE); + SetStatusText( + wxT("Value: ") + ToUnicode(intToStr(object)) + wxT(" ") + + ToUnicode(object_descs[object]), siBRUSH_VALUE); +} + +void MainWindow::onMenuBrushResource(wxCommandEvent &e) { + uncheckBrush(); + menuBrushResource->Check(e.GetId(), true); + resource = e.GetId() - miBrushResource - 1; + enabledGroup = ctResource; + currentBrush = btResource; + SetStatusText(wxT("Brush: Resource"), siBRUSH_TYPE); + SetStatusText( + wxT("Value: ") + ToUnicode(intToStr(resource)) + wxT(" ") + + ToUnicode(resource_descs[resource]), siBRUSH_VALUE); +} + +void MainWindow::onMenuBrushStartLocation(wxCommandEvent &e) { + uncheckBrush(); + menuBrushStartLocation->Check(e.GetId(), true); + startLocation = e.GetId() - miBrushStartLocation; + enabledGroup = ctLocation; + currentBrush = btStartLocation; + SetStatusText(wxT("Brush: Start Locations"), siBRUSH_TYPE); + SetStatusText(wxT("Value: ") + ToUnicode(intToStr(startLocation)), siBRUSH_VALUE); +} + +void MainWindow::onMenuRadius(wxCommandEvent &e) { + uncheckRadius(); + menuRadius->Check(e.GetId(), true); + radius = e.GetId() - miRadius; + SetStatusText(wxT("Radius: ") + ToUnicode(intToStr(radius)), siBRUSH_RADIUS); +} + +void MainWindow::change(int x, int y) { + switch (enabledGroup) { + case ctHeight: + program->glestChangeMapHeight(x, y, height, radius); + break; + case ctSurface: + program->changeMapSurface(x, y, surface, radius); + break; + case ctObject: + program->changeMapObject(x, y, object, radius); + break; + case ctResource: + program->changeMapResource(x, y, resource, radius); + break; + case ctLocation: + program->changeStartLocation(x, y, startLocation - 1); + break; + case ctGradient: + program->pirateChangeMapHeight(x, y, height, radius); + break; + } +} + +void MainWindow::uncheckBrush() { + for (int i = 0; i < heightCount; ++i) { + menuBrushHeight->Check(miBrushHeight + i + 1, false); + } + for (int i = 0; i < heightCount; ++i) { + menuBrushGradient->Check(miBrushGradient + i + 1, false); + } + for (int i = 0; i < surfaceCount; ++i) { + menuBrushSurface->Check(miBrushSurface + i + 1, false); + } + for (int i = 0; i < objectCount; ++i) { + menuBrushObject->Check(miBrushObject + i + 1, false); + } + for (int i = 0; i < resourceCount; ++i) { + menuBrushResource->Check(miBrushResource + i + 1, false); + } + for (int i = 0; i < startLocationCount; ++i) { + menuBrushStartLocation->Check(miBrushStartLocation + i + 1, false); + } +} + +void MainWindow::uncheckRadius() { + for (int i = 1; i <= radiusCount; ++i) { + menuRadius->Check(miRadius + i, false); + } +} + + void MainWindow::onKeyDown(wxKeyEvent &e) { + if (currentBrush == btHeight || currentBrush == btGradient) { // 'height' brush + if (e.GetKeyCode() >= '0' && e.GetKeyCode() <= '5') { + height = e.GetKeyCode() - 48; // '0'-'5' == 0-5 + if (e.GetModifiers() == wxMOD_CONTROL) { // Ctrl means negative + height = -height ; + } + int id_offset = heightCount / 2 + height + 1; + if (currentBrush == btHeight) { + wxCommandEvent evt(wxEVT_NULL, miBrushHeight + id_offset); + onMenuBrushHeight(evt); + } else { + wxCommandEvent evt(wxEVT_NULL, miBrushGradient + id_offset); + onMenuBrushGradient(evt); + } + return; + } + } + if (currentBrush == btSurface) { // surface texture + if (e.GetKeyCode() >= '1' && e.GetKeyCode() <= '5') { + surface = e.GetKeyCode() - 48; // '1'-'5' == 1-5 + wxCommandEvent evt(wxEVT_NULL, miBrushSurface + surface); + onMenuBrushSurface(evt); + return; + } + } + if (currentBrush == btObject) { + bool valid = true; + if (e.GetKeyCode() >= '1' && e.GetKeyCode() <= '9') { + object = e.GetKeyCode() - 48; // '1'-'9' == 1-9 + } else if (e.GetKeyCode() == '0') { // '0' == 10 + object = 10; + } else if (e.GetKeyCode() == '-') { // '-' == 0 + object = 0; + } else { + valid = false; + } + if (valid) { + wxCommandEvent evt(wxEVT_NULL, miBrushObject + object + 1); + onMenuBrushObject(evt); + return; + } + } + if (currentBrush == btResource) { + if (e.GetKeyCode() >= '0' && e.GetKeyCode() <= '5') { + resource = e.GetKeyCode() - 48; // '0'-'5' == 0-5 + wxCommandEvent evt(wxEVT_NULL, miBrushResource + resource + 1); + onMenuBrushResource(evt); + return; + } + } + if (currentBrush == btStartLocation) { + if (e.GetKeyCode() >= '1' && e.GetKeyCode() <= '8') { + startLocation = e.GetKeyCode() - 48; // '1'-'8' == 0-7 + wxCommandEvent evt(wxEVT_NULL, miBrushStartLocation + startLocation); + onMenuBrushStartLocation(evt); + return; + } + } + if (e.GetKeyCode() == 'H') { + wxCommandEvent evt(wxEVT_NULL, miBrushHeight + height + heightCount / 2 + 1); + onMenuBrushHeight(evt); + } else if (e.GetKeyCode() == ' ') { + if( resourceUnderMouse != 0 ) + { + wxCommandEvent evt(wxEVT_NULL, miBrushResource + resourceUnderMouse + 1); + onMenuBrushResource(evt); + } + else + { + wxCommandEvent evt(wxEVT_NULL, miBrushObject + objectUnderMouse + 1); + onMenuBrushObject(evt); + } + } else if (e.GetKeyCode() == 'G') { + wxCommandEvent evt(wxEVT_NULL, miBrushGradient + height + heightCount / 2 + 1); + onMenuBrushGradient(evt); + } else if (e.GetKeyCode() == 'S') { + wxCommandEvent evt(wxEVT_NULL, miBrushSurface + surface); + onMenuBrushSurface(evt); + } else if (e.GetKeyCode() == 'O') { + wxCommandEvent evt(wxEVT_NULL, miBrushObject + object + 1); + onMenuBrushObject(evt); + } else if (e.GetKeyCode() == 'R') { + wxCommandEvent evt(wxEVT_NULL, miBrushResource + resource + 1); + onMenuBrushResource(evt); + } else if (e.GetKeyCode() == 'L') { + wxCommandEvent evt(wxEVT_NULL, miBrushStartLocation + startLocation + 1); + onMenuBrushStartLocation(evt); + } else { + e.Skip(); + } +} + +BEGIN_EVENT_TABLE(MainWindow, wxFrame) + + EVT_TIMER(-1, MainWindow::onTimer) + + EVT_CLOSE(MainWindow::onClose) + + // these are 'handled' by GlCanvas and funneled to these handlers + //EVT_LEFT_DOWN(MainWindow::onMouseDown) + //EVT_MOTION(MainWindow::onMouseMove) + //EVT_KEY_DOWN(MainWindow::onKeyDown) + + EVT_MENU(wxID_OPEN, MainWindow::onMenuFileLoad) + EVT_MENU(wxID_SAVE, MainWindow::onMenuFileSave) + EVT_MENU(wxID_SAVEAS, MainWindow::onMenuFileSaveAs) + EVT_MENU(wxID_EXIT, MainWindow::onMenuFileExit) + + EVT_MENU(miEditUndo, MainWindow::onMenuEditUndo) + EVT_MENU(miEditRedo, MainWindow::onMenuEditRedo) + EVT_MENU(miEditReset, MainWindow::onMenuEditReset) + EVT_MENU(miEditResetPlayers, MainWindow::onMenuEditResetPlayers) + EVT_MENU(miEditResize, MainWindow::onMenuEditResize) + EVT_MENU(miEditFlipX, MainWindow::onMenuEditFlipX) + EVT_MENU(miEditFlipY, MainWindow::onMenuEditFlipY) + EVT_MENU(miEditRandomizeHeights, MainWindow::onMenuEditRandomizeHeights) + EVT_MENU(miEditRandomize, MainWindow::onMenuEditRandomize) + EVT_MENU(miEditSwitchSurfaces, MainWindow::onMenuEditSwitchSurfaces) + EVT_MENU(miEditInfo, MainWindow::onMenuEditInfo) + EVT_MENU(miEditAdvanced, MainWindow::onMenuEditAdvanced) + + EVT_MENU(miMiscResetZoomAndPos, MainWindow::onMenuMiscResetZoomAndPos) + EVT_MENU(miMiscAbout, MainWindow::onMenuMiscAbout) + EVT_MENU(miMiscHelp, MainWindow::onMenuMiscHelp) + + EVT_MENU_RANGE(miBrushHeight + 1, miBrushHeight + heightCount, MainWindow::onMenuBrushHeight) + EVT_MENU_RANGE(miBrushGradient + 1, miBrushGradient + heightCount, MainWindow::onMenuBrushGradient) + EVT_MENU_RANGE(miBrushSurface + 1, miBrushSurface + surfaceCount, MainWindow::onMenuBrushSurface) + EVT_MENU_RANGE(miBrushObject + 1, miBrushObject + objectCount, MainWindow::onMenuBrushObject) + EVT_MENU_RANGE(miBrushResource + 1, miBrushResource + resourceCount, MainWindow::onMenuBrushResource) + EVT_MENU_RANGE(miBrushStartLocation + 1, miBrushStartLocation + startLocationCount, MainWindow::onMenuBrushStartLocation) + EVT_MENU_RANGE(miRadius, miRadius + radiusCount, MainWindow::onMenuRadius) +END_EVENT_TABLE() + +// ===================================================== +// class GlCanvas +// ===================================================== + +GlCanvas::GlCanvas(MainWindow * mainWindow, int* args) + : wxGLCanvas(mainWindow, -1, wxDefaultPosition, wxDefaultSize, 0, wxT("GLCanvas"), args) { + this->mainWindow = mainWindow; +} + +void GlCanvas::onMouseDown(wxMouseEvent &event) { + mainWindow->onMouseDown(event); +} + +void GlCanvas::onMouseMove(wxMouseEvent &event) { + mainWindow->onMouseMove(event); +} + + void GlCanvas::onKeyDown(wxKeyEvent &event) { + mainWindow->onKeyDown(event); + } + +BEGIN_EVENT_TABLE(GlCanvas, wxGLCanvas) + EVT_KEY_DOWN(GlCanvas::onKeyDown) + + EVT_LEFT_DOWN(GlCanvas::onMouseDown) + EVT_MOTION(GlCanvas::onMouseMove) +END_EVENT_TABLE() + +// =============================================== +// class SimpleDialog +// =============================================== + +void SimpleDialog::addValue(const string &key, const string &value) { + values.push_back(pair(key, value)); +} + +string SimpleDialog::getValue(const string &key) { + for (int i = 0; i < values.size(); ++i) { + if (values[i].first == key) { + return values[i].second; + } + } + return ""; +} + +void SimpleDialog::show() { + + Create(NULL, -1, wxT("Edit Values")); + + wxSizer *sizer = new wxFlexGridSizer(2); + + vector texts; + + for (Values::iterator it = values.begin(); it != values.end(); ++it) { + sizer->Add(new wxStaticText(this, -1, ToUnicode(it->first)), 0, wxALL, 5); + wxTextCtrl *text = new wxTextCtrl(this, -1, ToUnicode(it->second)); + sizer->Add(text, 0, wxALL, 5); + texts.push_back(text); + } + SetSizerAndFit(sizer); + + ShowModal(); + + for (int i = 0; i < texts.size(); ++i) { + values[i].second = texts[i]->GetValue().ToAscii(); + } +} + +// =============================================== +// class App +// =============================================== + +bool App::OnInit() { + string fileparam; + if(argc==2){ + fileparam = wxFNCONV(argv[1]); + } + + mainWindow = new MainWindow(); + mainWindow->Show(); + mainWindow->init(fileparam); + return true; +} + +int App::MainLoop() { + try { + return wxApp::MainLoop(); + } catch (const exception &e) { + wxMessageDialog(NULL, ToUnicode(e.what()), wxT("Exception"), wxOK | wxICON_ERROR).ShowModal(); + } + return 0; +} + +int App::OnExit() { + return 0; +} + +}// end namespace + +IMPLEMENT_APP(MapEditor::App) diff --git a/source/shared_lib/include/graphics/model.h b/source/shared_lib/include/graphics/model.h new file mode 100644 index 00000000..be4ebea0 --- /dev/null +++ b/source/shared_lib/include/graphics/model.h @@ -0,0 +1,172 @@ +// ============================================================== +// This file is part of Glest Shared Library (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _SHARED_GRAPHICS_MODEL_H_ +#define _SHARED_GRAPHICS_MODEL_H_ + +#include +#include + +#include "types.h" +#include "pixmap.h" +#include "texture_manager.h" +#include "texture.h" +#include "model_header.h" + +using std::string; +using std::map; +using std::pair; + +namespace Shared{ namespace Graphics{ + +class Model; +class Mesh; +class ShadowVolumeData; +class InterpolationData; +class TextureManager; + +// ===================================================== +// class Mesh +// +// Part of a 3D model +// ===================================================== + +class Mesh{ +private: + //mesh data + Texture2D *textures[meshTextureCount]; + string texturePaths[meshTextureCount]; + + //vertex data counts + uint32 frameCount; + uint32 vertexCount; + uint32 indexCount; + + //vertex data + Vec3f *vertices; + Vec3f *normals; + Vec2f *texCoords; + Vec3f *tangents; + uint32 *indices; + + //material data + Vec3f diffuseColor; + Vec3f specularColor; + float specularPower; + float opacity; + + //properties + bool twoSided; + bool customColor; + + InterpolationData *interpolationData; + +public: + //init & end + Mesh(); + ~Mesh(); + void init(); + void end(); + + //maps + const Texture2D *getTexture(int i) const {return textures[i];} + + //counts + uint32 getFrameCount() const {return frameCount;} + uint32 getVertexCount() const {return vertexCount;} + uint32 getIndexCount() const {return indexCount;} + uint32 getTriangleCount() const; + + //data + const Vec3f *getVertices() const {return vertices;} + const Vec3f *getNormals() const {return normals;} + const Vec2f *getTexCoords() const {return texCoords;} + const Vec3f *getTangents() const {return tangents;} + const uint32 *getIndices() const {return indices;} + + //material + const Vec3f &getDiffuseColor() const {return diffuseColor;} + const Vec3f &getSpecularColor() const {return specularColor;} + float getSpecularPower() const {return specularPower;} + float getOpacity() const {return opacity;} + + //properties + bool getTwoSided() const {return twoSided;} + bool getCustomTexture() const {return customColor;} + + //external data + const InterpolationData *getInterpolationData() const {return interpolationData;} + + //interpolation + void buildInterpolationData(); + void updateInterpolationData(float t, bool cycle) const; + void updateInterpolationVertices(float t, bool cycle) const; + + //load + void loadV2(const string &dir, FILE *f, TextureManager *textureManager); + void loadV3(const string &dir, FILE *f, TextureManager *textureManager); + void load(const string &dir, FILE *f, TextureManager *textureManager); + void save(const string &dir, FILE *f); + +private: + void computeTangents(); +}; + +// ===================================================== +// class Model +// +// 3D Model, than can be loaded from a g3d file +// ===================================================== + +class Model{ +private: + TextureManager *textureManager; + +private: + uint8 fileVersion; + uint32 meshCount; + Mesh *meshes; + +public: + //constructor & destructor + Model(); + virtual ~Model(); + virtual void init()= 0; + virtual void end()= 0; + + //data + void updateInterpolationData(float t, bool cycle) const; + void updateInterpolationVertices(float t, bool cycle) const; + void buildShadowVolumeData() const; + + //get + uint8 getFileVersion() const {return fileVersion;} + uint32 getMeshCount() const {return meshCount;} + const Mesh *getMesh(int i) const {return &meshes[i];} + + uint32 getTriangleCount() const; + uint32 getVertexCount() const; + + //io + void load(const string &path); + void save(const string &path); + void loadG3d(const string &path); + void saveS3d(const string &path); + + void setTextureManager(TextureManager *textureManager) {this->textureManager= textureManager;} + +private: + void buildInterpolationData() const; +}; + +}}//end namespace + +#endif diff --git a/source/shared_lib/include/util/properties.h b/source/shared_lib/include/util/properties.h new file mode 100644 index 00000000..98d0a89d --- /dev/null +++ b/source/shared_lib/include/util/properties.h @@ -0,0 +1,80 @@ +// ============================================================== +// This file is part of Glest Shared Library (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _SHARED_UTIL_PROPERTIES_H_ +#define _SHARED_UTIL_PROPERTIES_H_ + +#include +#include +#include + +using std::map; +using std::vector; +using std::string; +using std::pair; + +namespace Shared{ namespace Util{ + +// ===================================================== +// class Properties +// +/// ini-like file loader +// ===================================================== + +class Properties{ +private: + static const int maxLine= 1024; + +public: + typedef pair PropertyPair; + typedef map PropertyMap; + typedef vector PropertyVector; + +private: + PropertyVector propertyVector; + PropertyMap propertyMap; + string path; + +public: + void clear(); + void load(const string &path); + void save(const string &path); + + int getPropertyCount() {return propertyVector.size();} + string getKey(int i) {return propertyVector[i].first;} + string getString(int i) {return propertyVector[i].second;} + + bool getBool(const string &key, const char *defaultValueIfNotFound=NULL) const; + int getInt(const string &key, const char *defaultValueIfNotFound=NULL) const; + int getInt(const string &key, int min, int max, const char *defaultValueIfNotFound=NULL) const; + float getFloat(const string &key, const char *defaultValueIfNotFound=NULL) const; + float getFloat(const string &key, float min, float max, const char *defaultValueIfNotFound=NULL) const; + + const string getString(const string &key, const char *defaultValueIfNotFound=NULL) const; + + int getInt(const char *key,const char *defaultValueIfNotFound=NULL) const; + bool getBool(const char *key,const char *defaultValueIfNotFound=NULL) const; + float getFloat(const char *key,const char *defaultValueIfNotFound=NULL) const; + const string getString(const char *key,const char *defaultValueIfNotFound=NULL) const; + + void setInt(const string &key, int value); + void setBool(const string &key, bool value); + void setFloat(const string &key, float value); + void setString(const string &key, const string &value); + + string toString(); + + +}; + +}}//end namespace + +#endif diff --git a/source/shared_lib/sources/graphics/gl/opengl.cpp b/source/shared_lib/sources/graphics/gl/opengl.cpp new file mode 100644 index 00000000..85bb0cbc --- /dev/null +++ b/source/shared_lib/sources/graphics/gl/opengl.cpp @@ -0,0 +1,151 @@ +// ============================================================== +// This file is part of Glest Shared Library (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "opengl.h" + +#include + +#include "graphics_interface.h" +#include "context_gl.h" +#include "gl_wrap.h" +#include "leak_dumper.h" + +using namespace Shared::Platform; +using namespace std; + +namespace Shared{ namespace Graphics{ namespace Gl{ + +// ===================================================== +// class Globals +// ===================================================== + +bool isGlExtensionSupported(const char *extensionName){ + const char *s; + GLint len; + const GLubyte *extensionStr= glGetString(GL_EXTENSIONS); + + s= reinterpret_cast(extensionStr); + len= strlen(extensionName); + + if(s != NULL) { + while ((s = strstr (s, extensionName)) != NULL) { + s+= len; + if((*s == ' ') || (*s == '\0')) { + return true; + } + } + } + + return false; +} + +bool isGlVersionSupported(int major, int minor, int release){ + + const char *strVersion= getGlVersion(); + + //major + const char *majorTok= strVersion; + int majorSupported= atoi(majorTok); + + if(majorSupportedmajor){ + return true; + } + + //minor + int i=0; + while(strVersion[i]!='.'){ + ++i; + } + const char *minorTok= &strVersion[i]+1; + int minorSupported= atoi(minorTok); + + if(minorSupportedminor){ + return true; + } + + //release + ++i; + while(strVersion[i]!='.'){ + ++i; + } + const char *releaseTok= &strVersion[i]+1; + + if(atoi(releaseTok)(glGetString(GL_VERSION)); +} + +const char *getGlRenderer(){ + return reinterpret_cast(glGetString(GL_RENDERER)); +} + +const char *getGlVendor(){ + return reinterpret_cast(glGetString(GL_VENDOR)); +} + +const char *getGlExtensions(){ + return reinterpret_cast(glGetString(GL_EXTENSIONS)); +} + +const char *getGlPlatformExtensions(){ + Context *c= GraphicsInterface::getInstance().getCurrentContext(); + return getPlatformExtensions(static_cast(c)->getPlatformContextGl()); +} + +int getGlMaxLights(){ + int i; + glGetIntegerv(GL_MAX_LIGHTS, &i); + return i; +} + +int getGlMaxTextureSize(){ + int i; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &i); + return i; +} + +int getGlMaxTextureUnits(){ + int i; + glGetIntegerv(GL_MAX_TEXTURE_UNITS, &i); + return i; +} + +int getGlModelviewMatrixStackDepth(){ + int i; + glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, &i); + return i; +} + +int getGlProjectionMatrixStackDepth(){ + int i; + glGetIntegerv(GL_MAX_PROJECTION_STACK_DEPTH, &i); + return i; +} + +void checkGlExtension(const char *extensionName){ + if(!isGlExtensionSupported(extensionName)){ + throw runtime_error("OpenGL extension not supported: " + string(extensionName)); + } +} + +}}}// end namespace diff --git a/source/shared_lib/sources/graphics/gl/texture_gl.cpp b/source/shared_lib/sources/graphics/gl/texture_gl.cpp new file mode 100644 index 00000000..cdedf6f1 --- /dev/null +++ b/source/shared_lib/sources/graphics/gl/texture_gl.cpp @@ -0,0 +1,373 @@ +// ============================================================== +// This file is part of Glest Shared Library (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "texture_gl.h" + +#include + +#include "opengl.h" +#include "leak_dumper.h" + +using namespace std; + +namespace Shared{ namespace Graphics{ namespace Gl{ + +using namespace Platform; + +GLint toWrapModeGl(Texture::WrapMode wrapMode){ + switch(wrapMode){ + case Texture::wmClamp: + return GL_CLAMP; + case Texture::wmRepeat: + return GL_REPEAT; + case Texture::wmClampToEdge: + return GL_CLAMP_TO_EDGE; + default: + assert(false); + return GL_CLAMP; + } +} + +GLint toFormatGl(Texture::Format format, int components){ + switch(format){ + case Texture::fAuto: + switch(components){ + case 1: + return GL_LUMINANCE; + case 3: + return GL_RGB; + case 4: + return GL_RGBA; + default: + assert(false); + return GL_RGBA; + } + break; + case Texture::fLuminance: + return GL_LUMINANCE; + case Texture::fAlpha: + return GL_ALPHA; + case Texture::fRgb: + return GL_RGB; + case Texture::fRgba: + return GL_RGBA; + default: + assert(false); + return GL_RGB; + } +} + +GLint toInternalFormatGl(Texture::Format format, int components){ + switch(format){ + case Texture::fAuto: + switch(components){ + case 1: + return GL_LUMINANCE8; + case 3: + return GL_RGB8; + case 4: + return GL_RGBA8; + default: + assert(false); + return GL_RGBA8; + } + break; + case Texture::fLuminance: + return GL_LUMINANCE8; + case Texture::fAlpha: + return GL_ALPHA8; + case Texture::fRgb: + return GL_RGB8; + case Texture::fRgba: + return GL_RGBA8; + default: + assert(false); + return GL_RGB8; + } +} + +// ===================================================== +// class Texture1DGl +// ===================================================== + +void Texture1DGl::init(Filter filter, int maxAnisotropy){ + assertGl(); + + if(!inited){ + + //params + GLint wrap= toWrapModeGl(wrapMode); + GLint glFormat= toFormatGl(format, pixmap.getComponents()); + GLint glInternalFormat= toInternalFormatGl(format, pixmap.getComponents()); + + //pixel init var + const uint8* pixels= pixmapInit? pixmap.getPixels(): NULL; + + //gen texture + glGenTextures(1, &handle); + glBindTexture(GL_TEXTURE_1D, handle); + + //wrap params + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, wrap); + + //maxAnisotropy + if(isGlExtensionSupported("GL_EXT_texture_filter_anisotropic")){ + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAX_ANISOTROPY_EXT, maxAnisotropy); + } + + if(mipmap){ + GLuint glFilter= filter==fTrilinear? GL_LINEAR_MIPMAP_LINEAR: GL_LINEAR_MIPMAP_NEAREST; + + //build mipmaps + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, glFilter); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + int error= gluBuild1DMipmaps( + GL_TEXTURE_1D, glInternalFormat, pixmap.getW(), + glFormat, GL_UNSIGNED_BYTE, pixels); + + if(error!=0){ + throw runtime_error("Error building texture 1D mipmaps"); + } + } + else{ + //build single texture + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexImage1D( + GL_TEXTURE_1D, 0, glInternalFormat, pixmap.getW(), + 0, glFormat, GL_UNSIGNED_BYTE, pixels); + + GLint error= glGetError(); + if(error!=GL_NO_ERROR){ + throw runtime_error("Error creating texture 1D"); + } + } + inited= true; + } + + assertGl(); +} + +void Texture1DGl::end(){ + if(inited){ + assertGl(); + glDeleteTextures(1, &handle); + assertGl(); + } +} + +// ===================================================== +// class Texture2DGl +// ===================================================== + +void Texture2DGl::init(Filter filter, int maxAnisotropy){ + assertGl(); + + if(!inited){ + + //params + GLint wrap= toWrapModeGl(wrapMode); + GLint glFormat= toFormatGl(format, pixmap.getComponents()); + GLint glInternalFormat= toInternalFormatGl(format, pixmap.getComponents()); + + //pixel init var + const uint8* pixels= pixmapInit? pixmap.getPixels(): NULL; + + //gen texture + glGenTextures(1, &handle); + glBindTexture(GL_TEXTURE_2D, handle); + + //wrap params + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap); + + //maxAnisotropy + if(isGlExtensionSupported("GL_EXT_texture_filter_anisotropic")){ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, maxAnisotropy); + } + + if(mipmap){ + GLuint glFilter= filter==fTrilinear? GL_LINEAR_MIPMAP_LINEAR: GL_LINEAR_MIPMAP_NEAREST; + + //build mipmaps + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, glFilter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + int error= gluBuild2DMipmaps( + GL_TEXTURE_2D, glInternalFormat, + pixmap.getW(), pixmap.getH(), + glFormat, GL_UNSIGNED_BYTE, pixels); + + if(error!=0){ + throw runtime_error("Error building texture 2D mipmaps"); + } + } + else{ + //build single texture + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexImage2D( + GL_TEXTURE_2D, 0, glInternalFormat, + pixmap.getW(), pixmap.getH(), + 0, glFormat, GL_UNSIGNED_BYTE, pixels); + + GLint error= glGetError(); + if(error!=GL_NO_ERROR){ + throw runtime_error("Error creating texture 2D"); + } + } + inited= true; + } + + assertGl(); +} + +void Texture2DGl::end(){ + if(inited){ + assertGl(); + glDeleteTextures(1, &handle); + assertGl(); + } +} + +// ===================================================== +// class Texture3DGl +// ===================================================== + +void Texture3DGl::init(Filter filter, int maxAnisotropy){ + assertGl(); + + if(!inited){ + + //params + GLint wrap= toWrapModeGl(wrapMode); + GLint glFormat= toFormatGl(format, pixmap.getComponents()); + GLint glInternalFormat= toInternalFormatGl(format, pixmap.getComponents()); + + //pixel init var + const uint8* pixels= pixmapInit? pixmap.getPixels(): NULL; + + //gen texture + glGenTextures(1, &handle); + glBindTexture(GL_TEXTURE_3D, handle); + + //wrap params + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, wrap); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, wrap); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, wrap); + + //build single texture + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexImage3D( + GL_TEXTURE_3D, 0, glInternalFormat, + pixmap.getW(), pixmap.getH(), pixmap.getD(), + 0, glFormat, GL_UNSIGNED_BYTE, pixels); + + GLint error= glGetError(); + if(error!=GL_NO_ERROR){ + throw runtime_error("Error creating texture 3D"); + } + inited= true; + } + + assertGl(); +} + +void Texture3DGl::end(){ + if(inited){ + assertGl(); + glDeleteTextures(1, &handle); + assertGl(); + } +} + +// ===================================================== +// class TextureCubeGl +// ===================================================== + +void TextureCubeGl::init(Filter filter, int maxAnisotropy){ + assertGl(); + + if(!inited){ + + //gen texture + glGenTextures(1, &handle); + glBindTexture(GL_TEXTURE_CUBE_MAP, handle); + + //wrap + GLint wrap= toWrapModeGl(wrapMode); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, wrap); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, wrap); + + //filter + if(mipmap){ + GLuint glFilter= filter==fTrilinear? GL_LINEAR_MIPMAP_LINEAR: GL_LINEAR_MIPMAP_NEAREST; + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, glFilter); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + else{ + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + + for(int i=0; i<6; ++i){ + //params + const Pixmap2D *currentPixmap= pixmap.getFace(i); + + GLint glFormat= toFormatGl(format, currentPixmap->getComponents()); + GLint glInternalFormat= toInternalFormatGl(format, currentPixmap->getComponents()); + + //pixel init var + const uint8* pixels= pixmapInit? currentPixmap->getPixels(): NULL; + GLenum target= GL_TEXTURE_CUBE_MAP_POSITIVE_X + i; + + if(mipmap){ + int error= gluBuild2DMipmaps( + target, glInternalFormat, + currentPixmap->getW(), currentPixmap->getH(), + glFormat, GL_UNSIGNED_BYTE, pixels); + + if(error!=0){ + throw runtime_error("Error building texture cube mipmaps"); + } + } + else{ + glTexImage2D( + target, 0, glInternalFormat, + currentPixmap->getW(), currentPixmap->getH(), + 0, glFormat, GL_UNSIGNED_BYTE, pixels); + } + + if(glGetError()!=GL_NO_ERROR){ + throw runtime_error("Error creating texture cube"); + } + } + inited= true; + + } + + assertGl(); +} + +void TextureCubeGl::end(){ + if(inited){ + assertGl(); + glDeleteTextures(1, &handle); + assertGl(); + } +} + +}}}//end namespace diff --git a/source/shared_lib/sources/graphics/model.cpp b/source/shared_lib/sources/graphics/model.cpp new file mode 100644 index 00000000..58e148a0 --- /dev/null +++ b/source/shared_lib/sources/graphics/model.cpp @@ -0,0 +1,525 @@ +// ============================================================== +// This file is part of Glest Shared Library (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "model.h" + +#include +#include +#include + +#include "interpolation.h" +#include "conversion.h" +#include "util.h" +#include "leak_dumper.h" + +using namespace Shared::Platform; + +using namespace std; + +namespace Shared{ namespace Graphics{ + +using namespace Util; + +// ===================================================== +// class Mesh +// ===================================================== + +// ==================== constructor & destructor ==================== + +Mesh::Mesh(){ + frameCount= 0; + vertexCount= 0; + indexCount= 0; + + vertices= NULL; + normals= NULL; + texCoords= NULL; + tangents= NULL; + indices= NULL; + interpolationData= NULL; + + for(int i=0; iupdate(t, cycle); +} + +void Mesh::updateInterpolationVertices(float t, bool cycle) const{ + interpolationData->updateVertices(t, cycle); +} + +// ==================== load ==================== + +void Mesh::loadV2(const string &dir, FILE *f, TextureManager *textureManager){ + //read header + MeshHeaderV2 meshHeader; + fread(&meshHeader, sizeof(MeshHeaderV2), 1, f); + + + if(meshHeader.normalFrameCount!=meshHeader.vertexFrameCount){ + throw runtime_error("Old model: vertex frame count different from normal frame count"); + } + + if(meshHeader.texCoordFrameCount!=1){ + throw runtime_error("Old model: texture coord frame count is not 1"); + } + + //init + frameCount= meshHeader.vertexFrameCount; + vertexCount= meshHeader.pointCount; + indexCount= meshHeader.indexCount; + + init(); + + //misc + twoSided= false; + customColor= false; + + //texture + if(meshHeader.hasTexture && textureManager!=NULL){ + texturePaths[mtDiffuse]= toLower(reinterpret_cast(meshHeader.texName)); + string texPath= dir+"/"+texturePaths[mtDiffuse]; + + textures[mtDiffuse]= static_cast(textureManager->getTexture(texPath)); + if(textures[mtDiffuse]==NULL){ + textures[mtDiffuse]= textureManager->newTexture2D(); + textures[mtDiffuse]->load(texPath); + } + } + + //read data + fread(vertices, sizeof(Vec3f)*frameCount*vertexCount, 1, f); + fread(normals, sizeof(Vec3f)*frameCount*vertexCount, 1, f); + if(textures[mtDiffuse]!=NULL){ + fread(texCoords, sizeof(Vec2f)*vertexCount, 1, f); + } + fread(&diffuseColor, sizeof(Vec3f), 1, f); + fread(&opacity, sizeof(float32), 1, f); + fseek(f, sizeof(Vec4f)*(meshHeader.colorFrameCount-1), SEEK_CUR); + fread(indices, sizeof(uint32)*indexCount, 1, f); +} + +void Mesh::loadV3(const string &dir, FILE *f, TextureManager *textureManager){ + //read header + MeshHeaderV3 meshHeader; + fread(&meshHeader, sizeof(MeshHeaderV3), 1, f); + + + if(meshHeader.normalFrameCount!=meshHeader.vertexFrameCount){ + throw runtime_error("Old model: vertex frame count different from normal frame count"); + } + + //init + frameCount= meshHeader.vertexFrameCount; + vertexCount= meshHeader.pointCount; + indexCount= meshHeader.indexCount; + + init(); + + //misc + twoSided= (meshHeader.properties & mp3TwoSided) != 0; + customColor= (meshHeader.properties & mp3CustomColor) != 0; + + //texture + if(!(meshHeader.properties & mp3NoTexture) && textureManager!=NULL){ + texturePaths[mtDiffuse]= toLower(reinterpret_cast(meshHeader.texName)); + string texPath= dir+"/"+texturePaths[mtDiffuse]; + + textures[mtDiffuse]= static_cast(textureManager->getTexture(texPath)); + if(textures[mtDiffuse]==NULL){ + textures[mtDiffuse]= textureManager->newTexture2D(); + textures[mtDiffuse]->load(texPath); + } + } + + //read data + fread(vertices, sizeof(Vec3f)*frameCount*vertexCount, 1, f); + fread(normals, sizeof(Vec3f)*frameCount*vertexCount, 1, f); + if(textures[mtDiffuse]!=NULL){ + for(int i=0; i(cMapPath)); + + string mapFullPath= dir + "/" + mapPath; + + textures[i]= static_cast(textureManager->getTexture(mapFullPath)); + if(textures[i]==NULL){ + textures[i]= textureManager->newTexture2D(); + if(meshTextureChannelCount[i]!=-1){ + textures[i]->getPixmap()->init(meshTextureChannelCount[i]); + } + textures[i]->load(mapFullPath); + } + } + flag*= 2; + } + + //read data + fread(vertices, sizeof(Vec3f)*frameCount*vertexCount, 1, f); + fread(normals, sizeof(Vec3f)*frameCount*vertexCount, 1, f); + if(meshHeader.textures!=0){ + fread(texCoords, sizeof(Vec2f)*vertexCount, 1, f); + } + fread(indices, sizeof(uint32)*indexCount, 1, f); + + //tangents + if(textures[mtNormal]!=NULL){ + computeTangents(); + } +} + +void Mesh::save(const string &dir, FILE *f){ + /*MeshHeader meshHeader; + meshHeader.vertexFrameCount= vertexFrameCount; + meshHeader.normalFrameCount= normalFrameCount; + meshHeader.texCoordFrameCount= texCoordFrameCount; + meshHeader.colorFrameCount= colorFrameCount; + meshHeader.pointCount= pointCount; + meshHeader.indexCount= indexCount; + meshHeader.properties= 0; + + if(twoSided) meshHeader.properties|= mpTwoSided; + if(customTexture) meshHeader.properties|= mpCustomTexture; + + if(texture==NULL){ + meshHeader.properties|= mpNoTexture; + meshHeader.texName[0]= '\0'; + } + else{ + strcpy(reinterpret_cast(meshHeader.texName), texName.c_str()); + texture->getPixmap()->saveTga(dir+"/"+texName); + } + + fwrite(&meshHeader, sizeof(MeshHeader), 1, f); + fwrite(vertices, sizeof(Vec3f)*vertexFrameCount*pointCount, 1, f); + fwrite(normals, sizeof(Vec3f)*normalFrameCount*pointCount, 1, f); + fwrite(texCoords, sizeof(Vec2f)*texCoordFrameCount*pointCount, 1, f); + fwrite(colors, sizeof(Vec4f)*colorFrameCount, 1, f); + fwrite(indices, sizeof(uint32)*indexCount, 1, f);*/ +} + +void Mesh::computeTangents(){ + delete [] tangents; + tangents= new Vec3f[vertexCount]; + for(int i=0; i(fileHeader.id), "G3D", 3)!=0){ + + printf("In [%s::%s] file = [%s] fileheader.id = [%s][%c]\n",__FILE__,__FUNCTION__,path.c_str(),reinterpret_cast(fileHeader.id),fileHeader.id[0]); + throw runtime_error("Not a valid S3D model"); + } + fileVersion= fileHeader.version; + + //version 4 + if(fileHeader.version==4){ + + //model header + ModelHeader modelHeader; + fread(&modelHeader, sizeof(ModelHeader), 1, f); + meshCount= modelHeader.meshCount; + if(modelHeader.type!=mtMorphMesh){ + throw runtime_error("Invalid model type"); + } + + //load meshes + meshes= new Mesh[meshCount]; + for(uint32 i=0; i + +#include "graphics_interface.h" +#include "graphics_factory.h" + +#include "leak_dumper.h" + +namespace Shared{ namespace Graphics{ + +// ===================================================== +// class TextureManager +// ===================================================== + +TextureManager::TextureManager(){ + textureFilter= Texture::fBilinear; + maxAnisotropy= 1; +} + +TextureManager::~TextureManager(){ + end(); +} + +void TextureManager::init(){ + for(int i=0; iinit(textureFilter, maxAnisotropy); + } +} + +void TextureManager::end(){ + for(int i=0; iend(); + delete textures[i]; + } + textures.clear(); +} + +void TextureManager::setFilter(Texture::Filter textureFilter){ + this->textureFilter= textureFilter; +} + +void TextureManager::setMaxAnisotropy(int maxAnisotropy){ + this->maxAnisotropy= maxAnisotropy; +} + +Texture *TextureManager::getTexture(const string &path){ + for(int i=0; igetPath()==path){ + return textures[i]; + } + } + return NULL; +} + +Texture1D *TextureManager::newTexture1D(){ + Texture1D *texture1D= GraphicsInterface::getInstance().getFactory()->newTexture1D(); + textures.push_back(texture1D); + + return texture1D; +} + +Texture2D *TextureManager::newTexture2D(){ + Texture2D *texture2D= GraphicsInterface::getInstance().getFactory()->newTexture2D(); + textures.push_back(texture2D); + + return texture2D; +} + +Texture3D *TextureManager::newTexture3D(){ + Texture3D *texture3D= GraphicsInterface::getInstance().getFactory()->newTexture3D(); + textures.push_back(texture3D); + + return texture3D; +} + + +TextureCube *TextureManager::newTextureCube(){ + TextureCube *textureCube= GraphicsInterface::getInstance().getFactory()->newTextureCube(); + textures.push_back(textureCube); + + return textureCube; +} + +}}//end namespace diff --git a/source/shared_lib/sources/util/conversion.cpp b/source/shared_lib/sources/util/conversion.cpp new file mode 100644 index 00000000..052e52b1 --- /dev/null +++ b/source/shared_lib/sources/util/conversion.cpp @@ -0,0 +1,126 @@ +// ============================================================== +// This file is part of Glest Shared Library (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "conversion.h" + +#include +#include +#include + +#include "leak_dumper.h" + +using namespace std; + +namespace Shared{ namespace Util{ + +const int strSize = 256; + +bool strToBool(const string &s){ + if (s=="0" || s=="false"){ + return false; + } + if (s=="1" || s=="true"){ + return true; + } + throw runtime_error("Error converting string to bool, expected 0 or 1, found: [" + s + "]"); +} + +int strToInt(const string &s){ + char *endChar; + int intValue= strtol(s.c_str(), &endChar, 10); + + if(*endChar!='\0'){ + throw runtime_error("Error converting from string to int, found: [" + s + "]"); + } + + return intValue; +} + + +float strToFloat(const string &s){ + char *endChar; + float floatValue= static_cast(strtod(s.c_str(), &endChar)); + + if(*endChar!='\0'){ + throw runtime_error("Error converting from string to float, found: [" + s + "]"); + } + + return floatValue; +} + +bool strToBool(const string &s, bool *b){ + if (s=="0" || s=="false"){ + *b= false; + return true; + } + if (s=="1" || s=="true"){ + *b= true; + return true; + } + + return false; +} + +bool strToInt(const string &s, int *i){ + char *endChar; + *i= strtol(s.c_str(), &endChar, 10); + + if(*endChar!='\0'){ + return false; + } + return true; +} + +bool strToFloat(const string &s, float *f){ + char *endChar; + *f= static_cast(strtod(s.c_str(), &endChar)); + + if(*endChar!='\0'){ + return false; + } + return true; +} + +string boolToStr(bool b){ + if(b){ + return "1"; + } + else{ + return "0"; + } +} + +string intToStr(int i){ + char str[strSize]; + sprintf(str, "%d", i); + return str; +} + +string intToHex(int i){ + char str[strSize]; + sprintf(str, "%x", i); + return str; +} + +string floatToStr(float f){ + char str[strSize]; + sprintf(str, "%.2f", f); + return str; +} + +string doubleToStr(double d){ + char str[strSize]; + sprintf(str, "%.2f", d); + return str; +} + + +}}//end namespace diff --git a/source/shared_lib/sources/util/properties.cpp b/source/shared_lib/sources/util/properties.cpp new file mode 100644 index 00000000..b8fef901 --- /dev/null +++ b/source/shared_lib/sources/util/properties.cpp @@ -0,0 +1,224 @@ +// ============================================================== +// This file is part of Glest Shared Library (www.glest.org) +// +// Copyright (C) 2001-2007 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "properties.h" + +#include +#include +#include + +#include "conversion.h" +#include "leak_dumper.h" + +using namespace std; + +namespace Shared{ namespace Util{ + +// ===================================================== +// class Properties +// ===================================================== + +void Properties::load(const string &path){ + + ifstream fileStream; + char lineBuffer[maxLine]; + string line, key, value; + int pos; + + this->path= path; + + fileStream.open(path.c_str(), ios_base::in); + if(fileStream.fail()){ + throw runtime_error("Can't open propertyMap file: " + path); + } + + propertyMap.clear(); + while(!fileStream.eof()){ + fileStream.getline(lineBuffer, maxLine); + lineBuffer[maxLine-1]='\0'; + + //process line if it it not a comment + if(lineBuffer[0]!=';'){ + + // gracefully handle win32 \r\n line endings + size_t len= strlen(lineBuffer); + if(len > 0 && lineBuffer[len-1] == '\r'){ + lineBuffer[len-1]= 0; + } + + line= lineBuffer; + pos= line.find('='); + + if(pos != string::npos){ + key= line.substr(0, pos); + value= line.substr(pos+1); + propertyMap.insert(PropertyPair(key, value)); + propertyVector.push_back(PropertyPair(key, value)); + } + } + } + + fileStream.close(); +} + +void Properties::save(const string &path){ + ofstream fileStream; + + fileStream.open(path.c_str(), ios_base::out | ios_base::trunc); + + fileStream << "; === propertyMap File === \n"; + fileStream << '\n'; + + for(PropertyMap::iterator pi= propertyMap.begin(); pi!=propertyMap.end(); ++pi){ + fileStream << pi->first << '=' << pi->second << '\n'; + } + + fileStream.close(); +} + +void Properties::clear(){ + propertyMap.clear(); + propertyVector.clear(); +} + +bool Properties::getBool(const string &key, const char *defaultValueIfNotFound) const{ + try{ + return strToBool(getString(key,defaultValueIfNotFound)); + } + catch(exception &e){ + throw runtime_error("Error accessing value: " + key + " in: " + path+"\n[" + e.what() + "]"); + } +} + +int Properties::getInt(const string &key,const char *defaultValueIfNotFound) const{ + try{ + return strToInt(getString(key,defaultValueIfNotFound)); + } + catch(exception &e){ + throw runtime_error("Error accessing value: " + key + " in: " + path + "\n[" + e.what() + "]"); + } +} + +int Properties::getInt(const string &key, int min, int max,const char *defaultValueIfNotFound) const{ + int i= getInt(key,defaultValueIfNotFound); + if(imax){ + throw runtime_error("Value out of range: " + key + ", min: " + intToStr(min) + ", max: " + intToStr(max)); + } + return i; +} + +float Properties::getFloat(const string &key, const char *defaultValueIfNotFound) const{ + try{ + return strToFloat(getString(key,defaultValueIfNotFound)); + } + catch(exception &e){ + throw runtime_error("Error accessing value: " + key + " in: " + path + "\n[" + e.what() + "]"); + } +} + +float Properties::getFloat(const string &key, float min, float max, const char *defaultValueIfNotFound) const{ + float f= getFloat(key,defaultValueIfNotFound); + if(fmax){ + throw runtime_error("Value out of range: " + key + ", min: " + floatToStr(min) + ", max: " + floatToStr(max)); + } + return f; +} + +const string Properties::getString(const string &key, const char *defaultValueIfNotFound) const{ + PropertyMap::const_iterator it; + it= propertyMap.find(key); + if(it==propertyMap.end()){ + if(defaultValueIfNotFound != NULL) { + return string(defaultValueIfNotFound); + } + else { + throw runtime_error("Value not found in propertyMap: " + key + ", loaded from: " + path); + } + } + else{ + return it->second; + } +} + +void Properties::setInt(const string &key, int value){ + setString(key, intToStr(value)); +} + +void Properties::setBool(const string &key, bool value){ + setString(key, boolToStr(value)); +} + +void Properties::setFloat(const string &key, float value){ + setString(key, floatToStr(value)); +} + +void Properties::setString(const string &key, const string &value){ + propertyMap.erase(key); + propertyMap.insert(PropertyPair(key, value)); +} + +string Properties::toString(){ + string rStr; + + for(PropertyMap::iterator pi= propertyMap.begin(); pi!=propertyMap.end(); pi++) + rStr+= pi->first + "=" + pi->second + "\n"; + + return rStr; +} + + + + +bool Properties::getBool(const char *key, const char *defaultValueIfNotFound) const{ + try{ + return strToBool(getString(key,defaultValueIfNotFound)); + } + catch(exception &e){ + throw runtime_error("Error accessing value: " + string(key) + " in: " + path+"\n[" + e.what() + "]"); + } +} + +int Properties::getInt(const char *key,const char *defaultValueIfNotFound) const{ + try{ + return strToInt(getString(key,defaultValueIfNotFound)); + } + catch(exception &e){ + throw runtime_error("Error accessing value: " + string(key) + " in: " + path + "\n[" + e.what() + "]"); + } +} + +float Properties::getFloat(const char *key, const char *defaultValueIfNotFound) const{ + try{ + return strToFloat(getString(key,defaultValueIfNotFound)); + } + catch(exception &e){ + throw runtime_error("Error accessing value: " + string(key) + " in: " + path + "\n[" + e.what() + "]"); + } +} + +const string Properties::getString(const char *key, const char *defaultValueIfNotFound) const{ + PropertyMap::const_iterator it; + it= propertyMap.find(key); + if(it==propertyMap.end()){ + if(defaultValueIfNotFound != NULL) { + return string(defaultValueIfNotFound); + } + else { + throw runtime_error("Value not found in propertyMap: " + string(key) + ", loaded from: " + path); + } + } + else{ + return it->second; + } +} + + +}}//end namepsace