#include "mapEditorAppState.h" #include "gameManager.h" #include "defConfigs.h" #include "cameraController.h" #include "unitFrameController.h" #include "player.h" #include "map.h" #include "mesh.h" #include "util.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace battleship{ using namespace tinyxml2; using namespace configData; using namespace gameBase; using namespace vb01; using namespace vb01Gui; using namespace std; using namespace std::filesystem; MapEditorAppState::MapEditor::MapEditor(string name, Vector2 size, bool newMap){ this->newMap = newMap; this->mapSize = size; map = Map::getSingleton(); map->load(name, newMap); if(newMap){ map->addPlayer(new Player(0, 0, 0)); map->addSpawnPoint(Vector3::VEC_ZERO); generatePlane(size); } string basePath = GameManager::getSingleton()->getPath(); prepareTextures(basePath + "Textures/Skyboxes/", true, skyTextures); prepareTextures(basePath + "Textures/Landmass/", false, landmassTextures); prepareTextures(basePath + "Textures/Water/", false, waterTextures); AssetManager *assetManager = AssetManager::getSingleton(); assetManager->load(basePath + "Models/Units/", true); assetManager->load(basePath + DEFAULT_TEXTURE); } void MapEditorAppState::MapEditor::updateCircleRadius(bool increase){ circleRadius += (increase ? INCREASE_RATE : -INCREASE_RATE); if(circleRadius > MAX_RADIUS) circleRadius = MAX_RADIUS; else if(circleRadius < MIN_RADIUS) circleRadius = MIN_RADIUS; } void MapEditorAppState::MapEditor::toggleSelection(TerrainObject *obj, bool select){ obj->node->getMesh(0)->setWireframe(select); selectedTerrainObject = (select ? obj : nullptr); } void MapEditorAppState::MapEditor::castSelectionRay(){ Camera *cam = Root::getSingleton()->getCamera(); Vector3 startPos = cam->getPosition(); Vector3 endPos = screenToSpace(getCursorPos()); vector results; Map *map = Map::getSingleton(); Ray::retrieveCollisions(startPos, (endPos - startPos).norm(), map->getNodeParent(), results); Ray::sortResults(results); if(selectedTerrainObject) toggleSelection(selectedTerrainObject, false); if(!results.empty()){ for(int i = 0; i < map->getNumTerrainObjects(); i++) if(map->getTerrainObject(i).node == results[i].mesh->getNode()){ toggleSelection(&map->getTerrainObject(i), true); break; } } } void MapEditorAppState::MapEditor::createWaterbody(){ Material *mat = new Material(Root::getSingleton()->getLibPath() + "texture"); mat->addBoolUniform("lightingEnabled", false); mat->addBoolUniform("texturingEnabled", true); mat->addTexUniform("textures[0]", waterTextures[0], false); Vector3 size = Vector3(10, 10, 1); Quad *quad = new Quad(size, true); quad->setMaterial(mat); Vector3 pos = 0.1 * Vector3::VEC_J; Node *node = new Node(pos); node->attachMesh(quad); Map *map = Map::getSingleton(); TerrainObject::Type type = TerrainObject::RECT_WATERBODY; map->addTerrainObject(TerrainObject(pos, size, Vector3(14, 7, 14), type, node, 0, nullptr, nullptr)); int objId = map->getNumTerrainObjects() - 1; TerrainObject &obj = map->getTerrainObject(objId); toggleSelection(&obj, true); prepareTerrainObjects(objId - 1, 1); prepareCellMarkers(obj); } void MapEditorAppState::MapEditor::moveTerrainObject(float strength){ Vector3 pos = selectedTerrainObject->node->getPosition(); switch(movementAxis){ case MovementAxis::X_AXIS: pos += strength * Vector3::VEC_I; break; case MovementAxis::Y_AXIS: pos += strength * Vector3::VEC_J; break; case MovementAxis::Z_AXIS: pos += strength * Vector3::VEC_K; break; } selectedTerrainObject->node->setPosition(pos); selectedTerrainObject->pos = pos; } void MapEditorAppState::MapEditor::pushLandmassVerts(float strength){ Mesh *mesh = map->getTerrainObject(0).node->getMesh(0); MeshData meshData = mesh->getMeshBase(); MeshData::Vertex *verts = meshData.vertices; int numVerts = meshData.numTris * 3; for(int i = 0; i < numVerts; i++){ Vector3 distVec = verts[i].pos - pushPos; distVec.y = 0; float dist = distVec.getLength(); if(dist < circleRadius) verts[i].pos.y += 10 * strength * dist / circleRadius; } mesh->updateVerts(meshData); } void MapEditorAppState::MapEditor::generatePlane(Vector2 size){ Root *root = Root::getSingleton(); Material *mat = new Material(root->getLibPath() + "texture"); mat->addBoolUniform("texturingEnabled", false); mat->addBoolUniform("lightingEnabled", false); mat->addVec4Uniform("diffuseColor", Vector4(1, 0, 0, 1)); Quad *mesh = new Quad(Vector3(size.x, size.y, 0), true, NUM_SUBDIVS, NUM_SUBDIVS); mesh->setMaterial(mat); Node *node = new Node(); node->attachMesh(mesh); TerrainObject::Type type = TerrainObject::LANDMASS; Vector3 pos = Vector3::VEC_ZERO, s = Vector3(size.x, 0, size.y); map->addTerrainObject(TerrainObject(pos, s, Vector3(14, 6, 14), type, node, 0, nullptr, nullptr)); prepareTerrainObjects(0, 1); prepareCellMarkers(map->getTerrainObject(0)); } void MapEditorAppState::MapEditor::prepareTextures(string basePath, bool skybox, vector &textures){ AssetManager *assetManager = AssetManager::getSingleton(); assetManager->load(basePath, skybox); vector texturePaths; for(int i = 0; i < assetManager->getNumAssets(); i++){ string assetPath = assetManager->getAsset(i)->path; if(assetPath.length() >= basePath.length() && assetPath.substr(0, basePath.length()) == basePath) texturePaths.push_back(assetPath); } if(skybox){ const int numTex = 6; for(int i = 0; i < texturePaths.size() / numTex; i++){ string paths[]{ texturePaths[i * numTex], texturePaths[i * numTex + 1], texturePaths[i * numTex + 2], texturePaths[i * numTex + 3], texturePaths[i * numTex + 4], texturePaths[i * numTex + 5], }; textures.push_back(new Texture(paths, numTex, true)); } } else{ for(int i = 0; i < texturePaths.size(); i++){ string paths[]{texturePaths[i]}; textures.push_back(new Texture(paths, 1, false)); } } } void MapEditorAppState::MapEditor::parseLandmass(){ XMLDocument *doc = new XMLDocument(); char *nodeTagName = "node"; XMLElement *rootEl = doc->NewElement(nodeTagName); rootEl->SetAttribute("name", "terrain"); XMLNode *rootTag = doc->InsertEndChild(rootEl); XMLElement *nodeEl = doc->NewElement(nodeTagName); nodeEl->SetAttribute("name", "plane"); XMLNode *nodeTag = rootTag->InsertEndChild(nodeEl); XMLElement *meshEl = doc->NewElement("mesh"); MeshData meshData = map->getTerrainObject(0).node->getMesh(0)->getMeshBase(); meshEl->SetAttribute("name", "mesh"); meshEl->SetAttribute("num_faces", meshData.numTris); meshEl->SetAttribute("num_vertex_groups", 0); meshEl->SetAttribute("num_shape_keys", 0); XMLNode *meshTag = nodeTag->InsertEndChild(meshEl); //TODO optimize vertex position extraction /* const int numSideQuads = NUM_SUBDIVS + 1, numSideVerts = numSideQuads + 1; Vector2 subQuadSize = Vector2(size.x / numSideQuads, size.y / numSideQuads); */ int numVerts = 3 * meshData.numTris; for(int i = 0; i < numVerts; i++){ XMLElement *vertEl = doc->NewElement("vertdata"); vertEl->SetAttribute("px", meshData.vertices[i].pos.x); vertEl->SetAttribute("py", meshData.vertices[i].pos.y); vertEl->SetAttribute("pz", meshData.vertices[i].pos.z); vertEl->SetAttribute("nx", meshData.vertices[i].norm.x); vertEl->SetAttribute("ny", meshData.vertices[i].norm.y); vertEl->SetAttribute("nz", meshData.vertices[i].norm.z); XMLNode *vertNode = meshTag->InsertEndChild(vertEl); } for(int i = 0; i < numVerts; i++){ XMLElement *vertEl = doc->NewElement("vert"); vertEl->SetAttribute("id", i); vertEl->SetAttribute("uvx", meshData.vertices[i].uv.x); vertEl->SetAttribute("uvy", meshData.vertices[i].uv.y); vertEl->SetAttribute("tx", meshData.vertices[i].tan.x); vertEl->SetAttribute("ty", meshData.vertices[i].tan.y); vertEl->SetAttribute("tz", meshData.vertices[i].tan.z); vertEl->SetAttribute("bx", meshData.vertices[i].biTan.x); vertEl->SetAttribute("by", meshData.vertices[i].biTan.y); vertEl->SetAttribute("bz", meshData.vertices[i].biTan.z); XMLNode *vertTag = meshTag->InsertEndChild(vertEl); } string name = GameManager::getSingleton()->getPath() + "Models/Maps/" + map->getMapName() + "/" + map->getMapName() + ".xml"; doc->SaveFile(name.c_str()); } void MapEditorAppState::MapEditor::deleteWeights(){ weightsGenerated = false; } void MapEditorAppState::MapEditor::prepareTerrainObject(u32 **weights, Cell *cells, int cellsByDim[3], float height, bool land){ const int numCells = cellsByDim[0] * cellsByDim[1] * cellsByDim[2]; Vector3 initPos = -.5 * Vector3(mapSize.x, -height, mapSize.y); Node *landmass = map->getTerrainObject(0).node; for(int i = 0; i < numCells; i++){ int xId = i % cellsByDim[0]; int yId = int(i / (cellsByDim[0] * cellsByDim[2])); int zId = (int(i / cellsByDim[0])) % cellsByDim[2]; cells[i].pos = initPos + Vector3(xId * cellLength, -yId * cellDepth, zId * cellWidth); if(land){ vector res; Ray::retrieveCollisions(Vector3(cells[i].pos.x, 100, cells[i].pos.z), -Vector3::VEC_J, landmass, res); Ray::sortResults(res); if(!res.empty()) cells[i].pos.y = res[0].pos.y; } } MeshData meshData = landmass->getMesh(0)->getMeshBase(); for(int i = 0; i < numCells; i++){ int waterbodyId = -1; bool impassible = false; TerrainObject waterbody; for(int j = 0; j < 3 * meshData.numTris; j++){ Vector3 point = Vector3(float(meshData.vertices[j].pos.x), float(meshData.vertices[j].pos.y), float(meshData.vertices[j].pos.z)); for(int k = 1; k < map->getNumTerrainObjects(); k++){ TerrainObject wb = map->getTerrainObject(k); bool diffX = (abs(point.x - wb.pos.x) < .5 * wb.size.x); bool diffY = (abs(point.y - wb.pos.z) < .5 * wb.size.z); bool diffZ = (abs(point.z + wb.pos.y) < .5 * wb.size.y); if(diffX && diffZ){ waterbodyId = k; break; } } if(waterbodyId != -1) waterbody = map->getTerrainObject(waterbodyId); bool withinX = (abs(point.x - cells[i].pos.x) < 0.5 * cellLength); bool withinY = (!land ? (abs(point.y - cells[i].pos.y) < 0.5 * cellDepth) : true); bool withinZ = (abs(point.z - cells[i].pos.z) < 0.5 * cellWidth); bool pointWithin = (withinX && withinY && withinZ); if(land && pointWithin && (waterbodyId != -1 && point.y < waterbody.pos.z)) impassible = true; else if(!land && pointWithin && waterbodyId != -1) impassible = true; } } for(int i = 0; i < numCells; i++){ int xId = i % cellsByDim[0]; int yId = i / (cellsByDim[0] * cellsByDim[2]); int zId = (i / cellsByDim[0]) % cellsByDim[2]; for(int j = 0; j < numCells; j++){ bool adjacent = false; if(xId == 0 && j - i == 1) adjacent = true; else if(xId == cellsByDim[0] - 1 && j - i == -1) adjacent = true; else if(0 < xId && xId < cellsByDim[0] - 1 && abs(j - i) == 1) adjacent = true; if(yId == 0 && j - i == cellsByDim[0] * cellsByDim[2]) adjacent = true; else if(0 < yId && yId < cellsByDim[1] - 1 && abs(j - i) == cellsByDim[0] * cellsByDim[2]) adjacent = true; else if(yId == cellsByDim[1] - 1 && j - i == -(cellsByDim[0] * cellsByDim[2])) adjacent = true; if(zId == 0 && j - i == cellsByDim[0]) adjacent = true; else if(zId == cellsByDim[2] - 1 && j - i == -cellsByDim[0]) adjacent = true; else if(0 < zId && zId < cellsByDim[2] - 1 && abs(j - i) == cellsByDim[0]) adjacent = true; weights[i][j] = IMPASS_NODE_VAL; if(i == j) weights[i][j] = 0; else if(adjacent) weights[i][j] = 1; } } } void MapEditorAppState::MapEditor::prepareTerrainObjects(int startId, int numObjs){ if(numObjs == -1) numObjs = map->getNumTerrainObjects(); for(int i = startId; i < numObjs; i++){ TerrainObject &obj = map->getTerrainObject(i); Vector3 size = obj.size; int cellsByDim[]{ int(size.x / cellLength), i == 0 ? 1 : int(size.y / cellDepth), int(size.z / cellWidth) }; const int numCells = cellsByDim[0] * cellsByDim[1] * cellsByDim[2]; Cell *cells = new Cell[numCells]; u32 **weights = new u32*[numCells]; for(int i = 0; i < numCells; i++) weights[i] = new u32[numCells]; obj.cells = cells; obj.numCells = numCells; obj.weights = weights; prepareTerrainObject(weights, cells, cellsByDim, (i > 0 ? obj.pos.y : 0), i == 0); } } string MapEditorAppState::MapEditor::parseTerrainObject(TerrainObject &terrObj){ string terrObjStr = "{\n"; if(terrObj.type = TerrainObject::LANDMASS) terrObjStr += "model = \"" + map->getMapName() + ".xml\",\n"; terrObjStr += "albedoMap = \"" + map->getMapName() + ".jpg\",\n"; terrObjStr += "size = {x = " + to_string(terrObj.pos.x) + ", y = " + to_string(terrObj.pos.y) + ", z = " + to_string(terrObj.pos.z) + "},\n"; terrObjStr += "nodes = {\n"; terrObjStr += "numCells = " + to_string(terrObj.numCells) + ",\n"; terrObjStr += "size = {x = " + to_string(cellLength) + ", y = " + to_string(cellDepth) + ", z = " + to_string(cellWidth) + "},\n"; terrObjStr += "pos = {\n"; for(int i = 0; i < terrObj.numCells; i++) terrObjStr += "{x = " + to_string(terrObj.cells[i].pos.x) + ", y = " + to_string(terrObj.cells[i].pos.y) + ", z = " + to_string(terrObj.cells[i].pos.z) + "},\n"; terrObjStr += "},\nimpassible = {\n"; for(int i = 0; i < terrObj.numCells; i++){ terrObjStr += (terrObj.cells[i].impassible ? "true" : "false"); terrObjStr += ",\n"; } terrObjStr += "},\nweights = {\n"; for(int i = 0; i < terrObj.numCells; i++) for(int j = 0; j < terrObj.numCells; j++) terrObjStr += to_string(terrObj.weights[i][j]) + ", "; terrObjStr += "}\n}\n},\n"; return terrObjStr; } void MapEditorAppState::MapEditor::parseMapScript(){ int numTerrObjs = map->getNumTerrainObjects(); string mapScript = "map = {\nnumWaterBodies = " + to_string(numTerrObjs) + ",\n"; mapScript += "impassibleNodeValue = " + to_string(IMPASS_NODE_VAL) + ",\n"; mapScript += "numPlayers = " + to_string(map->getNumPlayers()) + ",\n"; int numSpawnPoints = map->getNumSpawnPoints(); mapScript += "numSpawnPoints = " + to_string(numSpawnPoints) + ",\n"; mapScript += "spawnPoints = {\n"; for(int i = 0; i < numSpawnPoints; i++){ Vector3 sp = map->getSpawnPoint(i); mapScript += "{x = " + to_string(sp.x) + ", y = " + to_string(sp.y) + ", z = " + to_string(sp.z) + "},\n"; } mapScript += "},\nplayers = {\n"; for(int i = 0; i < map->getNumPlayers(); i++){ mapScript += "spawnPoint = 0,\n"; int numUnits = map->getPlayer(i)->getNumberOfUnits(); mapScript += "numUnits = " + to_string(numUnits) + ",\n"; if(numUnits > 0){ mapScript += "units = {\n"; for(int j = 0; j < numUnits; j++){ Unit *unit = map->getPlayer(i)->getUnit(j); string idStr = "id = " + to_string(unit->getId()); Vector3 unitPos = unit->getPos(); string posStr = "pos = {x = " + to_string(unitPos.x) + ", y = " + to_string(unitPos.y) + ", z = " + to_string(unitPos.z) + "}"; Quaternion unitRot = unit->getRot(); string rotStr = "rot = {w = " + to_string(unitRot.w) + ", x = " + to_string(unitRot.x) + ", y = " + to_string(unitRot.y) + ", z = " + to_string(unitRot.z) + "}"; mapScript += "{" + idStr + ", " + posStr + ", " + rotStr + "},\n"; } mapScript += "}\n,"; mapScript += "}"; } } mapScript += "},\n"; Root *root = Root::getSingleton(); string skyboxPath = ""; if(root->getSkybox()){ Texture *tex = ((Material::TextureUniform*)root->getSkybox()->getMaterial()->getUniform("tex"))->value; string path = tex->getPath()[0]; int slashId = path.find_last_of('/'); skyboxPath = path.substr(0, slashId); slashId = skyboxPath.find_last_of('/'); skyboxPath = skyboxPath.substr(slashId + 1); } mapScript += "skybox = \"" + skyboxPath + "\",\n"; mapScript += "terrain = " + parseTerrainObject(map->getTerrainObject(0)); mapScript += "waterbodies = {"; for(int i = 1; i < numTerrObjs; i++) mapScript += parseTerrainObject(map->getTerrainObject(i)); mapScript += "}\n}"; string file = GameManager::getSingleton()->getPath() + "Models/Maps/" + map->getMapName() + "/" + map->getMapName() + ".lua"; std::ofstream outFile(file); outFile << mapScript; outFile.close(); } void MapEditorAppState::MapEditor::toggleCellMarkers(){ cellMarkersVisible = !cellMarkersVisible; for(TerrainObject &obj : map->getTerrainObjects()) for(Node *cellMarker : obj.cellMarkers) cellMarker->setVisible(cellMarkersVisible); } void MapEditorAppState::MapEditor::prepareCellMarkers(TerrainObject &obj){ Root *root = Root::getSingleton(); Node *rootNode = root->getRootNode(); for(int i = 0; i < obj.numCells; i++){ Material *mat = new Material(root->getLibPath() + "texture"); mat->addBoolUniform("lightingEnabled", false); mat->addBoolUniform("texturingEnabled", false); mat->addVec4Uniform("diffuseColor", Vector4(0, 0, 1, 1)); Box *box = new Box(Vector3(1, 3, 1)); box->setMaterial(mat); Node *cellMarker = new Node(obj.cells[i].pos); cellMarker->attachMesh(box); cellMarker->setVisible(cellMarkersVisible); rootNode->attachChild(cellMarker); obj.cellMarkers.push_back(cellMarker); } } void MapEditorAppState::MapEditor::exportMap(){ string assetsPath = GameManager::getSingleton()->getPath(); string mapFolder = assetsPath + "Models/Maps/" + map->getMapName() + "/"; create_directory(mapFolder); copy_file(DEFAULT_TEXTURE, mapFolder + map->getMapName() + ".jpg"); prepareTerrainObjects(); parseLandmass(); parseMapScript(); } MapEditorAppState::MapEditorAppState(string name, Vector2 size, bool newMap) : AbstractAppState( AppStateType::MAP_EDITOR, configData::calcSumBinds(AppStateType::MAP_EDITOR, true), configData::calcSumBinds(AppStateType::MAP_EDITOR, false), GameManager::getSingleton()->getPath() + "Scripts/options.lua"){ mapName = name; mapSize = size; this->newMap = newMap; } void MapEditorAppState::update(){ radiusText->setText(L"Radius: " + to_wstring(mapEditor->getCircleRadius())); weightsText->setText(L"Weights generated: " + to_wstring(mapEditor->isWeightsGenerated())); CameraController *camCtr = CameraController::getSingleton(); if(!(camCtr->isLookingAround() || mapEditor->isPushing())) camCtr->updateCameraPosition(); UnitFrameController *ufCtr = UnitFrameController::getSingleton(); if(ufCtr->isPlacingFrames()) ufCtr->update(); } void MapEditorAppState::onAttached(){ AbstractAppState::onAttached(); mapEditor = new MapEditor(mapName, mapSize, newMap); Root *root = Root::getSingleton(); Material *mat = new Material(root->getLibPath() + "text"); mat->addBoolUniform("texturingEnabled", false); mat->addVec4Uniform("diffuseColor", Vector4::VEC_IJKL); string fontPath = GameManager::getSingleton()->getPath() + "Fonts/batang.ttf"; radiusText = new Text(fontPath, L""); radiusText->setMaterial(mat); weightsText = new Text(fontPath, L""); weightsText->setMaterial(mat); Node *radiusNode = new Node(Vector3(0, 100, 0)); radiusNode->addText(radiusText); root->getGuiNode()->attachChild(radiusNode); Node *weightsNode = new Node(Vector3(0, 200, 0)); weightsNode->addText(weightsText); root->getGuiNode()->attachChild(weightsNode); } void MapEditorAppState::onDettached(){} void MapEditorAppState::onAction(int bind, bool isPressed){ UnitFrameController *ufCtr = UnitFrameController::getSingleton(); switch((Bind)bind){ case Bind::LOOK_AROUND: if(ufCtr->isPlacingFrames()){ Player *player = Map::getSingleton()->getPlayer(0); UnitFrameController::UnitFrame &frame = ufCtr->getUnitFrame(0); player->addUnit(new Unit(player, frame.id, frame.model->getPosition(), frame.model->getOrientation())); } else CameraController::getSingleton()->setLookingAround(isPressed); break; case Bind::DESELECT_STRUCTURE: ufCtr->removeUnitFrames(); ufCtr->setPlacingFrames(false); break; case Bind::INCREASE_RADIUS: if(isPressed) mapEditor->updateCircleRadius(true); break; case Bind::DECREASE_RADIUS: if(isPressed) mapEditor->updateCircleRadius(false); break; case Bind::LEFT_CLICK: if(isPressed){ Camera *cam = Root::getSingleton()->getCamera(); Vector3 startPos = cam->getPosition(); Vector2 cursorPos = getCursorPos(); Vector3 endPos = screenToSpace(cursorPos); vector results; Ray::retrieveCollisions(startPos, (endPos - startPos).norm(), Map::getSingleton()->getTerrainObject(0).node, results); Ray::sortResults(results); if(cursorPos.y < GameManager::getSingleton()->getHeight() - mapEditor->getGuiThreshold()){ bool push = !results.empty(); mapEditor->setPushing(push); if(push) mapEditor->setPushPos(results[0].pos); mapEditor->castSelectionRay(); } else mapEditor->setPushing(false); } else mapEditor->setPushing(false); break; case Bind::CREATE_WATERBODY: if(isPressed) mapEditor->createWaterbody(); break; case Bind::MOVE_TERR_OBJ: if(isPressed) mapEditor->setMovingTerrainObject(true); break; case Bind::STOP_TERR_OBJ: if(isPressed) mapEditor->setMovingTerrainObject(false); break; case Bind::MOVE_X_AXIS: case Bind::MOVE_Y_AXIS: case Bind::MOVE_Z_AXIS: if(isPressed && mapEditor->isMovingTerrainObject()){ MapEditor::MovementAxis axis = MapEditor::MovementAxis((int)bind - (int)Bind::MOVE_X_AXIS); mapEditor->setMovementAxis(axis); } break; case Bind::GENERATE_WEIGHTS: mapEditor->prepareTerrainObjects(); break; case Bind::TOGGLE_CELL_MARKERS: if(isPressed) mapEditor->toggleCellMarkers(); break; } } void MapEditorAppState::onAnalog(int bind, float strength){ CameraController *camCtr = CameraController::getSingleton(); switch((Bind)bind){ case Bind::LOOK_UP: case Bind::LOOK_DOWN: if(camCtr->isLookingAround()){ Vector3 dirProj = Root::getSingleton()->getCamera()->getDirection(); dirProj = Vector3(dirProj.x, 0, dirProj.z).norm(); camCtr->orientCamera(Vector3(0, 1, 0).cross(dirProj), strength); } break; case Bind::LOOK_LEFT: case Bind::LOOK_RIGHT: if(camCtr->isLookingAround()) camCtr->orientCamera(Vector3(0, 1, 0), strength); break; case Bind::PUSH_VERTS_UP: case Bind::PUSH_VERTS_DOWN: if(mapEditor->isPushing()) mapEditor->pushLandmassVerts(strength); else if(mapEditor->isMovingTerrainObject() && mapEditor->getSelectedTerrainObject() != &Map::getSingleton()->getTerrainObject(0)) mapEditor->moveTerrainObject(strength); break; } } }