diff --git a/Assets/Scripts/resourceData.lua b/Assets/Scripts/resourceData.lua index 7188e29..efbc997 100644 --- a/Assets/Scripts/resourceData.lua +++ b/Assets/Scripts/resourceData.lua @@ -12,14 +12,14 @@ resources = { }, unitCornerPoints = { { - {x = .9, y = -1, z = -6}, - {x = -.9, y = -1, z = -6}, - {x = -.9, y = -1, z = 5.8}, - {x = .9, y = -1, z = 5.8}, - {x = .9, y = 3.5, z = -6}, - {x = -.9, y = 3.5, z = -6}, - {x = -.9, y = 3.5, z = 5.8}, - {x = .9, y = 3.5, z = 5.8} + {x = 1, y = .571, z = -1}, + {x = -1, y = .571, z = -1}, + {x = -1, y = .571, z = 1}, + {x = 1, y = .571, z = 1}, + {x = 1, y = .571, z = -1}, + {x = -1, y = .571, z = -1}, + {x = -1, y = .571, z = 1}, + {x = 1, y = .571, z = 1} } } } diff --git a/Assets/Scripts/unitData.lua b/Assets/Scripts/unitData.lua index 233cb74..a2774e3 100644 --- a/Assets/Scripts/unitData.lua +++ b/Assets/Scripts/unitData.lua @@ -40,14 +40,14 @@ units = { unitCornerPoints = { { - {x = .9, y = -1, z = -6}, - {x = -.9, y = -1, z = -6}, - {x = -.9, y = -1, z = 5.8}, - {x = .9, y = -1, z = 5.8}, - {x = .9, y = 3.5, z = -6}, - {x = -.9, y = 3.5, z = -6}, - {x = -.9, y = 3.5, z = 5.8}, - {x = .9, y = 3.5, z = 5.8} + {x = 1.45, y = -4.11, z = -1.21}, + {x = -1.45, y = -4.11, z = -1.21}, + {x = -1.45, y = -4.11, z = 1.21}, + {x = 1.45, y = -4.11, z = 1.21}, + {x = 1.45, y = 4.11, z = -1.21}, + {x = -1.45, y = 4.11, z = -1.21}, + {x = -1.45, y = 4.11, z = 1.21}, + {x = 1.45, y = 4.11, z = 1.21} }, { {x = .5, y = -.5, z = -7.05}, diff --git a/activeGameState.cpp b/activeGameState.cpp index a61b8fe..6f26dc6 100755 --- a/activeGameState.cpp +++ b/activeGameState.cpp @@ -63,7 +63,7 @@ namespace battleship{ mat->addBoolUniform("diffuseColorEnabled", false); mat->addVec4Uniform("diffuseColor", Vector4(.5, .5, .5, .5)); - dragbox = new Quad(Vector3::VEC_ZERO, false); + Quad *dragbox = new Quad(Vector3::VEC_ZERO, false); dragbox->setMaterial(mat); dragboxNode = new Node(); dragboxNode->attachMesh(dragbox); @@ -107,9 +107,15 @@ namespace battleship{ wealthText->getText(0)->setText(L"Wealth: " + to_wstring(mainPlayer->getWealth())); researchText->getText(0)->setText(L"Research: " + to_wstring(mainPlayer->getResearch())); - if (isSelectionBox) + if(!isSelectionBox && leftMouseClicked && getTime() - lastLeftMouseClicked > 10) + isSelectionBox = true; + else if (isSelectionBox) updateSelectionBox(); + dragboxNode->setVisible(isSelectionBox); + + updateGameObjHoveredOn(); + vector selectedUnits = mainPlayer->getSelectedUnits(); if(!selectingDestOrient && leftMouseClicked && !selectedUnits.empty() && getTime() - lastLeftMouseClicked > 100){ @@ -123,8 +129,6 @@ namespace battleship{ fc->addGameObjectFrame(GameObjectFrame(unit->getId(), GameObject::Type::UNIT)); } - dragboxNode->setVisible(isSelectionBox); - renderUnits(); CameraController *camCtr = CameraController::getSingleton(); @@ -138,6 +142,36 @@ namespace battleship{ ufCtr->update(); } + void ActiveGameState::updateGameObjHoveredOn(){ + vector gameObjs; + vector players = Game::getSingleton()->getPlayers(); + + for(Player *pl : players){ + vector resDeps = pl->getResourceDeposits(); + vector units = pl->getUnits(); + + for(ResourceDeposit *rd : resDeps) + gameObjs.push_back((GameObject*)rd); + + for(Unit *u : units) + gameObjs.push_back((GameObject*)u); + } + + Vector2 cursorPos = getCursorPos(); + + for(GameObject *gameObj : gameObjs){ + Vector2 rectSize = gameObj->calculateSelectionRect(); + Vector2 screenPos = gameObj->getScreenPos(); + + if(fabs(cursorPos.x - screenPos.x) < .5 * rectSize.x && fabs(cursorPos.y - screenPos.y) < .5 * rectSize.y){ + gameObjHoveredOn = gameObj; + return; + } + } + + gameObjHoveredOn = nullptr; + } + void ActiveGameState::deselectUnits(){ mainPlayer->deselectUnits(); GameObjectFrameController *ufCtr = GameObjectFrameController::getSingleton(); @@ -158,14 +192,11 @@ namespace battleship{ for (Unit *u : units) { if (u->getPlayer() == mainPlayer){ - Vector3 selectionBoxOrigin = dragboxNode->getPosition(); - Vector3 selectionBoxEnd = selectionBoxOrigin + dragbox->getSize(); + Vector3 dragboxSize = ((Quad*)dragboxNode->getMesh(0))->getSize(); + Vector3 dragboxOrigin = dragboxNode->getPosition(), dragboxEnd = dragboxOrigin + dragboxSize; Vector2 pos = u->getScreenPos(); - if(isSelectionBox && - pos.x >= selectionBoxOrigin.x && pos.x <= selectionBoxEnd.x && - pos.y >= selectionBoxOrigin.y && pos.y <= selectionBoxEnd.y){ - + if(isSelectionBox && fabs(pos.x - dragboxOrigin.x) < .5 * dragboxSize.x && fabs(pos.y - dragboxOrigin.y) < .5 * dragboxSize.y){ if(!u->isSelected()){ if(!shiftPressed) deselectUnits(); @@ -173,14 +204,12 @@ namespace battleship{ mainPlayer->selectUnit(u); u->toggleSelection(true); - if(engineersSelected()){ + if(unitClassSelected(UnitClass::ENGINEER)){ ConcreteGuiManager *guiManager = ConcreteGuiManager::getSingleton(); guiManager->readLuaScreenScript("engineerCommands.lua"); } } } - - u->getNode()->setVisible(true); } else{ Vector3 rendUnPos = u->getPos(); @@ -229,12 +258,14 @@ namespace battleship{ } Vector3 size = Vector3(selectionBoxEnd.x - selectionBoxOrigin.x, selectionBoxEnd.y - selectionBoxOrigin.y, 0); + Quad *dragbox = (Quad*)dragboxNode->getMesh(0); dragbox->setSize(size); + dragbox->updateVerts(dragbox->getMeshBase()); dragboxNode->setPosition(Vector3(selectionBoxOrigin.x, selectionBoxOrigin.y, 0)); } //TODO fix ejectable unit selection with multiple transports selected - void ActiveGameState::issueOrder(Order::TYPE type, bool addOrder) { + void ActiveGameState::issueOrder(Order::TYPE type, vector targets, bool addOrder) { Vector3 color; switch(type){ @@ -242,7 +273,6 @@ namespace battleship{ color = Vector3::VEC_J; break; case Order::TYPE::ATTACK: - case Order::TYPE::LAUNCH: color = Vector3::VEC_I; break; case Order::TYPE::PATROL: @@ -255,9 +285,7 @@ namespace battleship{ break; } - if(type == Order::TYPE::PATROL){ - } - else if(type == Order::TYPE::EJECT){ + if(type == Order::TYPE::EJECT){ vector units = mainPlayer->getSelectedUnits(); for(Unit *u : units){ @@ -268,53 +296,40 @@ namespace battleship{ targets.push_back(Order::Target((Unit*)slot.vehicle)); } } - else + else if(type != Order::TYPE::PATROL) targets.push_back(Order::Target()); LineRenderer *lineRenderer = LineRenderer::getSingleton(); for (int i = 0; i < mainPlayer->getNumSelectedUnits(); ++i) { Unit *u = mainPlayer->getSelectedUnit(i); + lineRenderer->addLine(u->getPos(), targets[i].pos, color); vector lines = lineRenderer->getLines(); + LineRenderer::Line line = lines[lines.size() - 1]; Vector3 destDir = (selectingDestOrient ? GameObjectFrameController::getSingleton()->getGameObjectFrame(0).getDirVec() : Vector3::VEC_ZERO); Order order(type, line, targets, destDir); - if (type != Order::TYPE::LAUNCH) { - if (addOrder) - u->addOrder(order); - else - u->setOrder(order); - } + if (addOrder) + u->addOrder(order); + else + u->setOrder(order); } - targets.clear(); + this->targets.clear(); } - void ActiveGameState::addTarget() { + void ActiveGameState::castRayToTerrain() { Camera *cam = Root::getSingleton()->getCamera(); Vector3 camPos = cam->getPosition(); Vector3 endPos = screenToSpace(getCursorPos()); Vector3 rayDir = (endPos - camPos).norm(); vector results = RayCaster::cast(camPos, rayDir, Map::getSingleton()->getNodeParent()); - std::map unitData; if(!results.empty()){ - vector ancestors = results[0].mesh->getNode()->getAncestors(); - Node *node = ancestors[ancestors.size() - 2]; - std::map::iterator it = unitData.find(node); - - Unit *unit = nullptr; GameObjectFrameController *ufCtr = GameObjectFrameController::getSingleton(); - - if(ufCtr->isPlacingFrames()){ - GameObjectFrame goFr = ufCtr->getGameObjectFrame(0); - unit = new Structure(mainPlayer, goFr.getId(), goFr.getModel()->getPosition(), goFr.getModel()->getOrientation()); - mainPlayer->addUnit(unit); - } - Map *map = Map::getSingleton(); for(Unit *u : mainPlayer->getSelectedUnits()){ @@ -340,52 +355,76 @@ namespace battleship{ } } - targets.push_back(Order::Target((it != unitData.end() ? unitData[node] : unit), results[0].pos)); + targets.push_back(Order::Target(nullptr, results[0].pos)); } } - bool ActiveGameState::engineersSelected(){ + bool ActiveGameState::unitClassSelected(UnitClass uc){ for(Unit *u : mainPlayer->getSelectedUnits()) - if(u->getUnitClass() == UnitClass::ENGINEER) + if(u->getUnitClass() == uc) return true; return false; } void ActiveGameState::onAction(int bind, bool isPressed) { - GameManager *gm = GameManager::getSingleton(); - InGameAppState *inGameState = (InGameAppState*) gm->getStateManager()->getAppStateByType((int)AppStateType::IN_GAME_STATE); GameObjectFrameController *ufCtr = GameObjectFrameController::getSingleton(); switch((Bind)bind){ case Bind::DRAG_BOX: - isSelectionBox = isPressed; leftMouseClicked = isPressed; - clickPoint = getCursorPos(); - if(isPressed) + if(isPressed){ + clickPoint = getCursorPos(); lastLeftMouseClicked = getTime(); + } else{ if (mainPlayer->getNumSelectedUnits() > 0){ - addTarget(); + if(selectingPatrolPoints){ + castRayToTerrain(); - if(!(targets.empty() || selectingPatrolPoints)){ - Order::TYPE type = Order::TYPE::MOVE; - - if(controlPressed || (targets[0].unit && targets[0].unit->getPlayer()->getSide() != mainPlayer->getSide())) - type = Order::TYPE::ATTACK; - else if(ufCtr->isPlacingFrames() && !selectingDestOrient) - type = Order::TYPE::BUILD; - - issueOrder(type, shiftPressed); + if(!shiftPressed){ + issueOrder(Order::TYPE::PATROL, targets, shiftPressed); + selectingPatrolPoints = false; + } } - else if(selectingPatrolPoints && !shiftPressed){ - issueOrder(Order::TYPE::PATROL, false); - selectingPatrolPoints = false; + else{ + bool canSelect = canSelectHoveredOnGameObj(); + + if(gameObjHoveredOn && gameObjHoveredOn->getPlayer()->getSide() != mainPlayer->getSide()) + issueOrder(Order::TYPE::ATTACK, vector{Order::Target((Unit*)gameObjHoveredOn, Vector3())}, shiftPressed); + else if(controlPressed){ + castRayToTerrain(); + issueOrder(Order::TYPE::ATTACK, targets, shiftPressed); + } + else if(ufCtr->isPlacingFrames() && !selectingDestOrient){ + castRayToTerrain(); + issueOrder(Order::TYPE::BUILD, targets, shiftPressed); + } + else if(canSelect){ + if(!shiftPressed) + deselectUnits(); + + mainPlayer->selectUnit((Unit*)gameObjHoveredOn); + } + else if(!canSelect){ + castRayToTerrain(); + issueOrder(Order::TYPE::MOVE, targets, shiftPressed); + } + } + } + else{ + if(canSelectHoveredOnGameObj()){ + if(!shiftPressed) + deselectUnits(); + + mainPlayer->selectUnit((Unit*)gameObjHoveredOn); } } selectingDestOrient = false; + isSelectionBox = false; + ufCtr->setPlacingFrames(false); ufCtr->setRotatingFrames(false); ufCtr->removeGameObjectFrames(); diff --git a/activeGameState.h b/activeGameState.h index c0f1c36..a4e5d26 100755 --- a/activeGameState.h +++ b/activeGameState.h @@ -4,6 +4,7 @@ #include "guiAppState.h" #include "player.h" #include "map.h" +#include "unit.h" namespace vb01{ class Node; @@ -14,6 +15,8 @@ namespace vb01Gui{ } namespace battleship{ + class GameObject; + class ActiveGameState : public gameBase::AbstractAppState { public: ActiveGameState(GuiAppState*, int); @@ -28,24 +31,24 @@ namespace battleship{ inline Player* getPlayer(){return mainPlayer;} inline std::vector& getUnitGroup(int i){return unitGroups[i];} private: + void updateGameObjHoveredOn(); void initDragbox(); vb01::Node* initText(vb01::Vector2); void deselectUnits(); void renderUnits(); void updateSelectionBox(); void updateStructureFrames(); - void addTarget(); - void issueOrder(Order::TYPE, bool); + void castRayToTerrain(); + void issueOrder(Order::TYPE, std::vector, bool); bool isInLineOfSight(vb01::Vector3, float, Unit*); - bool engineersSelected(); + bool unitClassSelected(UnitClass); + inline bool canSelectHoveredOnGameObj(){return gameObjHoveredOn && gameObjHoveredOn->isSelectable() && gameObjHoveredOn->getPlayer() == mainPlayer;} GuiAppState *guiState; Player *mainPlayer; - vb01::Quad *dragbox = nullptr; + GameObject *gameObjHoveredOn = nullptr; vb01::Node *dragboxNode = nullptr, *textNode = nullptr, *refinedsText = nullptr, *wealthText = nullptr, *researchText = nullptr; vb01::Vector2 clickPoint; - std::vector unitScreenPosVec; - std::vector unitLightNodes; std::vector unitGroups[9]; std::vector targets; std::vector buttons; diff --git a/gameObject.cpp b/gameObject.cpp index c879d62..2ec1519 100644 --- a/gameObject.cpp +++ b/gameObject.cpp @@ -20,6 +20,7 @@ namespace battleship{ leftVec = model->getGlobalAxis(0); upVec = model->getGlobalAxis(1); dirVec = model->getGlobalAxis(2); + screenPos = spaceToScreen(pos); } void GameObject::placeAt(Vector3 p) { @@ -33,6 +34,16 @@ namespace battleship{ } void GameObject::initProperties(){ + sol::table SOL_LUA_STATE = generateView()[GameObject::getGameObjTableName()]; + + for(int i = 0; i < 8; i++){ + sol::table cornerTable = SOL_LUA_STATE["unitCornerPoints"][id + 1][i + 1]; + corners[i] = Vector3(cornerTable["x"], cornerTable["y"], cornerTable["z"]); + } + + width = corners[0].x - corners[1].x; + height = corners[4].y - corners[0].y; + length = corners[3].z - corners[0].z; } void GameObject::destroyModel(){ @@ -101,4 +112,37 @@ namespace battleship{ return "resources"; } } + + int GameObject::sortCorners(vector &cornersOnScreen, bool vertical, bool max){ + const int NUM_CORNERS = cornersOnScreen.size(); + int ans = 0; + + for(int i = 0; i < NUM_CORNERS; i++){ + if(vertical && ((max && cornersOnScreen[ans].y < cornersOnScreen[i].y) || (!max && cornersOnScreen[ans].y > cornersOnScreen[i].y))) + ans = i; + else if(!vertical && ((max && cornersOnScreen[ans].x < cornersOnScreen[i].x) || (!max && cornersOnScreen[ans].x > cornersOnScreen[i].x))) + ans = i; + } + + return ans; + } + + Vector2 GameObject::calculateSelectionRect(){ + const int NUM_CORNERS = 8; + vector cornersOnScreen; + + for(int i = 0; i < NUM_CORNERS; i++){ + Vector3 cornerInWorld = leftVec * corners[i].x + upVec * corners[i].y + dirVec * corners[i].z; + cornersOnScreen.push_back(spaceToScreen(cornerInWorld)); + } + + int leftMostPointId = sortCorners(cornersOnScreen, false, false); + int rightMostPointId = sortCorners(cornersOnScreen, false, true); + int topMostPointId = sortCorners(cornersOnScreen, true, false); + int bottomMostPointId = sortCorners(cornersOnScreen, true, true); + + float sizeX = cornersOnScreen[rightMostPointId].x - cornersOnScreen[leftMostPointId].x; + float sizeY = cornersOnScreen[bottomMostPointId].y - cornersOnScreen[topMostPointId].y; + return Vector2(sizeX, sizeY); + } } diff --git a/gameObject.h b/gameObject.h index 2467345..d6678dd 100644 --- a/gameObject.h +++ b/gameObject.h @@ -24,6 +24,8 @@ namespace battleship{ void placeAt(vb01::Vector3); void orientAt(vb01::Quaternion); std::string getGameObjTableName(); + vb01::Vector2 calculateSelectionRect(); + inline vb01::Vector2 getScreenPos(){return screenPos;} inline vb01::Vector3 getCorner(int i){return corners[i];} inline bool isSelected(){return selected;} inline bool isSelectable(){return selectable;} @@ -42,6 +44,7 @@ namespace battleship{ inline int getId() {return id;} inline Type getType(){return type;} private: + int sortCorners(std::vector&, bool, bool); protected: virtual void initProperties(); virtual void destroyModel(); @@ -57,8 +60,9 @@ namespace battleship{ sf::Sound *deathSfx; vb01::Model *model = nullptr; vb01::Vector3 pos = vb01::Vector3(0, 0, 0), upVec = vb01::Vector3(0, 1, 0), dirVec = vb01::Vector3(0, 0, 1), leftVec = vb01::Vector3(1, 0, 0), corners[8]; + vb01::Vector2 screenPos; vb01::Quaternion rot; - bool selected = false, selectable, debugging = false; + bool selected = false, selectable = false, debugging = false; float width, height, length; }; } diff --git a/projectile.cpp b/projectile.cpp index 78be807..8a4eeca 100755 --- a/projectile.cpp +++ b/projectile.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include @@ -43,6 +42,8 @@ namespace battleship{ } void Projectile::initProperties(int weaponId){ + GameObject::initProperties(); + sol::table SOL_LUA_STATE = generateView()[GameObject::getGameObjTableName()]; rayLength = SOL_LUA_STATE["rayLength"][id + 1][weaponTypeId + 1][weaponId + 1]; damage = SOL_LUA_STATE["damage"][id + 1][weaponTypeId + 1][weaponId + 1]; diff --git a/resourceDeposit.cpp b/resourceDeposit.cpp index 4b9d848..9bd6da4 100644 --- a/resourceDeposit.cpp +++ b/resourceDeposit.cpp @@ -4,11 +4,9 @@ namespace battleship{ using namespace vb01; ResourceDeposit::ResourceDeposit(Player *player, int id, Vector3 pos, Quaternion rot) : GameObject(GameObject::Type::RESOURCE_DEPOSIT, id, player, pos, rot){ + initProperties(); initModel(); placeAt(pos); orientAt(rot); } - - void ResourceDeposit::update(){ - } } diff --git a/resourceDeposit.h b/resourceDeposit.h index 38667b7..14ca75e 100644 --- a/resourceDeposit.h +++ b/resourceDeposit.h @@ -7,7 +7,6 @@ namespace battleship{ class ResourceDeposit : public GameObject{ public: ResourceDeposit(Player*, int, vb01::Vector3, vb01::Quaternion); - void update(); private: }; } diff --git a/unit.cpp b/unit.cpp index dd1697f..ca0fb98 100755 --- a/unit.cpp +++ b/unit.cpp @@ -27,6 +27,7 @@ namespace battleship{ Unit::Unit(Player *player, int id, Vector3 pos, Quaternion rot) : GameObject(GameObject::Type::UNIT, id, player, pos, rot){ this->id = id; this->player = player; + selectable = true; init(); @@ -51,6 +52,8 @@ namespace battleship{ } void Unit::initProperties(){ + GameObject::initProperties(); + sol::table SOL_LUA_STATE = generateView()[GameObject::getGameObjTableName()]; string name = SOL_LUA_STATE["name"][id + 1]; health = SOL_LUA_STATE["health"][id + 1]; @@ -71,16 +74,6 @@ namespace battleship{ garrisonSlots.push_back(GarrisonSlot(bg, fg, pos)); } - string cornerInd = "unitCornerPoints"; - - for(int i = 0; i < 8; i++){ - sol::table cornerTable = SOL_LUA_STATE[cornerInd][id + 1][i + 1]; - corners[i] = Vector3(cornerTable["x"], cornerTable["y"], cornerTable["z"]); - } - - width = corners[0].x - corners[1].x; - height = corners[4].y - corners[0].y; - length = corners[3].z - corners[0].z; } void Unit::destroySound(){ @@ -151,7 +144,6 @@ namespace battleship{ executeOrders(); - screenPos = spaceToScreen(pos); displayUnitStats(hpForegroundNode, hpBackgroundNode, health, maxHealth); for(GarrisonSlot &slot : garrisonSlots) diff --git a/unit.h b/unit.h index c6a58ce..d858dba 100755 --- a/unit.h +++ b/unit.h @@ -73,8 +73,6 @@ namespace battleship{ virtual void addOrder(Order); virtual void reinit(); inline const std::vector& getGarrisonSlots(){return garrisonSlots;} - inline vb01::Vector3 getCorner(int i){return corners[i];} - inline vb01::Vector2 getScreenPos(){return screenPos;} inline vb01::Vector3* getPosPtr() {return &pos;} inline float getLineOfSight() {return lineOfSight;} inline vb01::Model* getNode() {return model;} @@ -94,9 +92,7 @@ namespace battleship{ protected: UnitClass unitClass; UnitType type; - vb01::Vector2 screenPos; std::vector orders; - vb01::Vector3 corners[8]; sf::SoundBuffer *fireSfxBuffer; sf::Sound *fireSfx = nullptr; int health, maxHealth, cost, id, playerId, lenHpBar = 200, rateOfFire; diff --git a/util.cpp b/util.cpp index b2137b3..2b7a846 100755 --- a/util.cpp +++ b/util.cpp @@ -242,7 +242,7 @@ namespace battleship{ return files; } - Vector2 spaceToScreen(Vector3 pos){ + Vector3 spaceToScreen3d(Vector3 pos){ Root *root = Root::getSingleton(); Camera *cam = root->getCamera(); Vector3 dir = cam->getDirection(), up = cam->getUp(); @@ -255,7 +255,13 @@ namespace battleship{ vec4 ndcPos = proj * view * vec4(pos.x, pos.y, pos.z, 1); ndcPos.x /= ndcPos.w; ndcPos.y /= ndcPos.w; - return Vector2(0.5 * width * (1 + ndcPos.x), 0.5 * height * (1 - ndcPos.y)); + ndcPos.z /= ndcPos.w; + return Vector3(0.5 * width * (1 + ndcPos.x), 0.5 * height * (1 - ndcPos.y), ndcPos.z); + } + + Vector2 spaceToScreen(Vector3 pos){ + Vector3 pos3d = spaceToScreen3d(pos); + return Vector2(pos3d.x, pos3d.y); } Vector3 screenToSpace(Vector2 pos){ diff --git a/util.h b/util.h index 4807351..57ffef9 100755 --- a/util.h +++ b/util.h @@ -27,6 +27,7 @@ namespace battleship{ void makeTitlescreenButtons(GuiAppState*); std::vector readDir(std::string, bool); vb01::Vector2 spaceToScreen(vb01::Vector3); + vb01::Vector3 spaceToScreen3d(vb01::Vector3); vb01::Vector3 screenToSpace(vb01::Vector2); }