1455 lines
52 KiB
C++
1455 lines
52 KiB
C++
// Copyright © 2008-2021 Pioneer Developers. See AUTHORS.txt for details
|
|
// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
|
|
|
|
#include "SectorView.h"
|
|
|
|
#include "AnimationCurves.h"
|
|
#include "Game.h"
|
|
#include "GameConfig.h"
|
|
#include "GameSaveError.h"
|
|
#include "Input.h"
|
|
#include "MathUtil.h"
|
|
#include "Pi.h"
|
|
#include "Player.h"
|
|
#include "Space.h"
|
|
#include "StringF.h"
|
|
#include "galaxy/Galaxy.h"
|
|
#include "galaxy/GalaxyCache.h"
|
|
#include "galaxy/Sector.h"
|
|
#include "galaxy/StarSystem.h"
|
|
#include "graphics/Graphics.h"
|
|
#include "graphics/Material.h"
|
|
#include "graphics/Renderer.h"
|
|
#include "gui/Gui.h"
|
|
#include "lua/LuaConstants.h"
|
|
#include "lua/LuaObject.h"
|
|
#include "sigc++/functors/mem_fun.h"
|
|
#include "utils.h"
|
|
#include <algorithm>
|
|
#include <sstream>
|
|
#include <unordered_set>
|
|
|
|
using namespace Graphics;
|
|
|
|
static const int DRAW_RAD = 5;
|
|
#define INNER_RADIUS (Sector::SIZE * 1.5f)
|
|
#define OUTER_RADIUS (Sector::SIZE * float(DRAW_RAD))
|
|
static const float FAR_THRESHOLD = 7.5f;
|
|
static const float FAR_LIMIT = 36.f;
|
|
static const float FAR_MAX = 46.f;
|
|
|
|
enum DetailSelection {
|
|
DETAILBOX_NONE = 0,
|
|
DETAILBOX_INFO = 1,
|
|
DETAILBOX_FACTION = 2
|
|
};
|
|
|
|
static const float ZOOM_SPEED = 15;
|
|
static const float WHEEL_SENSITIVITY = .03f; // Should be a variable in user settings.
|
|
|
|
REGISTER_INPUT_BINDING(SectorView)
|
|
{
|
|
using namespace InputBindings;
|
|
auto *mapView = input->GetBindingPage("MapControls");
|
|
Input::BindingGroup *group;
|
|
|
|
#define BINDING_GROUP(n) group = mapView->GetBindingGroup(#n);
|
|
#define KEY_BINDING(name, k1, k2) \
|
|
input->AddActionBinding(name, group, InputBindings::Action({ k1 }, { k2 }));
|
|
#define AXIS_BINDING(name, k1, k2) \
|
|
input->AddAxisBinding(name, group, InputBindings::Axis({}, { k1 }, { k2 }));
|
|
|
|
BINDING_GROUP(GeneralViewControls)
|
|
KEY_BINDING("BindResetOrientationAndZoom", SDLK_t, 0)
|
|
AXIS_BINDING("BindMapViewYaw", SDLK_KP_4, SDLK_KP_6)
|
|
AXIS_BINDING("BindMapViewPitch", SDLK_KP_8, SDLK_KP_2)
|
|
AXIS_BINDING("BindViewZoom", SDLK_KP_PLUS, SDLK_KP_MINUS)
|
|
AXIS_BINDING("BindMapViewMoveUp", SDLK_r, SDLK_f)
|
|
AXIS_BINDING("BindMapViewMoveLeft", SDLK_a, SDLK_d)
|
|
AXIS_BINDING("BindMapViewMoveForward", SDLK_w, SDLK_s)
|
|
|
|
BINDING_GROUP(SectorMapViewControls)
|
|
KEY_BINDING("BindMapToggleSelectionFollowView", SDLK_RETURN, SDLK_KP_ENTER)
|
|
KEY_BINDING("BindMapWarpToCurrentSystem", SDLK_c, 0)
|
|
KEY_BINDING("BindMapWarpToSelectedSystem", SDLK_g, 0)
|
|
|
|
#undef KEY_BINDING
|
|
#undef AXIS_BINDING
|
|
#undef BINDING_GROUP
|
|
}
|
|
|
|
void SectorView::InputBinding::RegisterBindings()
|
|
{
|
|
mapViewReset = AddAction("BindResetOrientationAndZoom");
|
|
mapToggleSelectionFollowView = AddAction("BindMapToggleSelectionFollowView");
|
|
mapWarpToCurrent = AddAction("BindMapWarpToCurrentSystem");
|
|
mapWarpToSelected = AddAction("BindMapWarpToSelectedSystem");
|
|
|
|
mapViewMoveForward = AddAxis("BindMapViewMoveForward");
|
|
mapViewMoveLeft = AddAxis("BindMapViewMoveLeft");
|
|
mapViewMoveUp = AddAxis("BindMapViewMoveUp");
|
|
|
|
mapViewYaw = AddAxis("BindMapViewYaw");
|
|
mapViewPitch = AddAxis("BindMapViewPitch");
|
|
mapViewZoom = AddAxis("BindMapViewZoom");
|
|
}
|
|
|
|
SectorView::SectorView(Game *game) :
|
|
PiGuiView("sector-view"),
|
|
InputBindings(Pi::input),
|
|
m_galaxy(game->GetGalaxy())
|
|
{
|
|
InitDefaults();
|
|
|
|
m_rotX = m_rotXMovingTo = m_rotXDefault;
|
|
m_rotZ = m_rotZMovingTo = m_rotZDefault;
|
|
m_zoom = m_zoomMovingTo = m_zoomDefault;
|
|
|
|
// XXX I have no idea if this is correct,
|
|
// I just copied it from the one other place m_zoomClamped is set
|
|
m_zoomClamped = Clamp(m_zoom, 1.f, FAR_LIMIT);
|
|
|
|
m_inSystem = true;
|
|
|
|
RefCountedPtr<StarSystem> system = Pi::game->GetSpace()->GetStarSystem();
|
|
m_current = system->GetPath();
|
|
assert(!m_current.IsSectorPath());
|
|
m_current = m_current.SystemOnly();
|
|
m_selected = m_hyperspaceTarget = system->GetStars()[0]->GetPath(); // XXX This always selects the first star of the system
|
|
|
|
GotoSystem(m_current);
|
|
m_pos = m_posMovingTo;
|
|
|
|
m_automaticSystemSelection = true;
|
|
m_detailBoxVisible = DETAILBOX_INFO;
|
|
m_toggledFaction = false;
|
|
|
|
InitObject();
|
|
}
|
|
|
|
SectorView::SectorView(const Json &jsonObj, Game *game) :
|
|
PiGuiView("sector-view"),
|
|
InputBindings(Pi::input),
|
|
m_galaxy(game->GetGalaxy())
|
|
{
|
|
InitDefaults();
|
|
|
|
try {
|
|
Json sectorViewObj = jsonObj["sector_view"];
|
|
|
|
m_pos.x = m_posMovingTo.x = sectorViewObj["pos_x"];
|
|
m_pos.y = m_posMovingTo.y = sectorViewObj["pos_y"];
|
|
m_pos.z = m_posMovingTo.z = sectorViewObj["pos_z"];
|
|
m_rotX = m_rotXMovingTo = sectorViewObj["rot_x"];
|
|
m_rotZ = m_rotZMovingTo = sectorViewObj["rot_z"];
|
|
m_zoom = m_zoomMovingTo = sectorViewObj["zoom"];
|
|
// XXX I have no idea if this is correct,
|
|
// I just copied it from the one other place m_zoomClamped is set
|
|
m_zoomClamped = Clamp(m_zoom, 1.f, FAR_LIMIT);
|
|
m_inSystem = sectorViewObj["in_system"];
|
|
m_current = SystemPath::FromJson(sectorViewObj["current"]);
|
|
m_selected = SystemPath::FromJson(sectorViewObj["selected"]);
|
|
m_hyperspaceTarget = SystemPath::FromJson(sectorViewObj["hyperspace"]);
|
|
m_automaticSystemSelection = sectorViewObj["automatic_system_selection"];
|
|
m_detailBoxVisible = sectorViewObj["detail_box_visible"];
|
|
} catch (Json::type_error &) {
|
|
throw SavedGameCorruptException();
|
|
}
|
|
|
|
InitObject();
|
|
}
|
|
|
|
void SectorView::InitDefaults()
|
|
{
|
|
m_rotXDefault = Pi::config->Float("SectorViewXRotation");
|
|
m_rotZDefault = Pi::config->Float("SectorViewZRotation");
|
|
m_zoomDefault = Pi::config->Float("SectorViewZoom");
|
|
m_rotXDefault = Clamp(m_rotXDefault, -170.0f, -10.0f);
|
|
m_zoomDefault = Clamp(m_zoomDefault, 0.1f, 5.0f);
|
|
m_previousSearch = "";
|
|
|
|
// Note: INT_MAX != (int) ((float) INT_MAX)
|
|
const float farPos = static_cast<float>(INT_MAX);
|
|
m_secPosFar = vector3f(farPos, farPos, farPos);
|
|
m_radiusFar = 0;
|
|
m_cacheXMin = 0;
|
|
m_cacheXMax = 0;
|
|
m_cacheYMin = 0;
|
|
m_cacheYMax = 0;
|
|
m_cacheYMin = 0;
|
|
m_cacheYMax = 0;
|
|
|
|
m_sectorCache = m_galaxy->NewSectorSlaveCache();
|
|
InputBindings.RegisterBindings();
|
|
|
|
m_drawRouteLines = true; // where should this go?!
|
|
m_route = std::vector<SystemPath>();
|
|
}
|
|
|
|
void SectorView::InitObject()
|
|
{
|
|
// single keystroke handlers
|
|
m_onToggleSelectionFollowView =
|
|
InputBindings.mapToggleSelectionFollowView->onPressed.connect([&]() {
|
|
m_automaticSystemSelection = !m_automaticSystemSelection;
|
|
});
|
|
m_onWarpToCurrent =
|
|
InputBindings.mapWarpToCurrent->onPressed.connect([&]() {
|
|
GotoSystem(m_current);
|
|
});
|
|
m_onWarpToSelected =
|
|
InputBindings.mapWarpToSelected->onPressed.connect([&]() {
|
|
GotoSystem(m_selected);
|
|
});
|
|
m_onViewReset =
|
|
InputBindings.mapViewReset->onPressed.connect([&]() {
|
|
while (m_rotZ < -180.0f)
|
|
m_rotZ += 360.0f;
|
|
while (m_rotZ > 180.0f)
|
|
m_rotZ -= 360.0f;
|
|
m_rotXMovingTo = m_rotXDefault;
|
|
m_rotZMovingTo = m_rotZDefault;
|
|
m_zoomMovingTo = m_zoomDefault;
|
|
});
|
|
|
|
m_onMouseWheelCon =
|
|
Pi::input->onMouseWheel.connect(sigc::mem_fun(this, &SectorView::MouseWheel));
|
|
|
|
SetTransparency(true);
|
|
|
|
m_lineVerts.reset(new Graphics::VertexArray(Graphics::ATTRIB_POSITION, 500));
|
|
m_secLineVerts.reset(new Graphics::VertexArray(Graphics::ATTRIB_POSITION, 500));
|
|
m_starVerts.reset(new Graphics::VertexArray(
|
|
Graphics::ATTRIB_POSITION | Graphics::ATTRIB_DIFFUSE | Graphics::ATTRIB_UV0, 500));
|
|
|
|
Gui::Screen::PushFont("OverlayFont");
|
|
m_clickableLabels = new Gui::LabelSet();
|
|
m_clickableLabels->SetLabelColor(Color(178, 178, 178, 191));
|
|
Add(m_clickableLabels, 0, 0);
|
|
Gui::Screen::PopFont();
|
|
|
|
Gui::Screen::PushFont("OverlayFont");
|
|
|
|
m_renderer = Pi::renderer; //XXX pass cleanly to all views constructors!
|
|
|
|
Graphics::RenderStateDesc rsd;
|
|
m_solidState = m_renderer->CreateRenderState(rsd);
|
|
|
|
rsd.blendMode = Graphics::BLEND_ALPHA;
|
|
rsd.depthWrite = false;
|
|
rsd.cullMode = CULL_NONE;
|
|
m_alphaBlendState = m_renderer->CreateRenderState(rsd);
|
|
|
|
Graphics::MaterialDescriptor bbMatDesc;
|
|
bbMatDesc.effect = Graphics::EFFECT_SPHEREIMPOSTOR;
|
|
m_starMaterial.Reset(m_renderer->CreateMaterial(bbMatDesc));
|
|
|
|
m_disk.reset(new Graphics::Drawables::Disk(m_renderer, m_solidState, Color::WHITE, 0.2f));
|
|
}
|
|
|
|
SectorView::~SectorView()
|
|
{
|
|
m_onMouseWheelCon.disconnect();
|
|
m_onToggleSelectionFollowView.disconnect();
|
|
m_onWarpToCurrent.disconnect();
|
|
m_onWarpToSelected.disconnect();
|
|
m_onViewReset.disconnect();
|
|
}
|
|
|
|
void SectorView::SaveToJson(Json &jsonObj)
|
|
{
|
|
Json sectorViewObj({}); // Create JSON object to contain sector view data.
|
|
|
|
sectorViewObj["pos_x"] = m_pos.x;
|
|
sectorViewObj["pos_y"] = m_pos.y;
|
|
sectorViewObj["pos_z"] = m_pos.z;
|
|
sectorViewObj["rot_x"] = m_rotX;
|
|
sectorViewObj["rot_z"] = m_rotZ;
|
|
sectorViewObj["zoom"] = m_zoom;
|
|
sectorViewObj["in_system"] = m_inSystem;
|
|
|
|
Json currentSystemObj({}); // Create JSON object to contain current system data.
|
|
m_current.ToJson(currentSystemObj);
|
|
sectorViewObj["current"] = currentSystemObj; // Add current system object to sector view object.
|
|
|
|
Json selectedSystemObj({}); // Create JSON object to contain selected system data.
|
|
m_selected.ToJson(selectedSystemObj);
|
|
sectorViewObj["selected"] = selectedSystemObj; // Add selected system object to sector view object.
|
|
|
|
Json hyperspaceSystemObj({}); // Create JSON object to contain hyperspace system data.
|
|
m_hyperspaceTarget.ToJson(hyperspaceSystemObj);
|
|
sectorViewObj["hyperspace"] = hyperspaceSystemObj; // Add hyperspace system object to sector view object.
|
|
|
|
sectorViewObj["automatic_system_selection"] = m_automaticSystemSelection;
|
|
sectorViewObj["detail_box_visible"] = m_detailBoxVisible;
|
|
|
|
jsonObj["sector_view"] = sectorViewObj; // Add sector view object to supplied object.
|
|
}
|
|
|
|
#define FFRAC(_x) ((_x)-floor(_x))
|
|
|
|
void SectorView::Draw3D()
|
|
{
|
|
PROFILE_SCOPED()
|
|
|
|
m_lineVerts->Clear();
|
|
m_secLineVerts->Clear();
|
|
m_clickableLabels->Clear();
|
|
m_starVerts->Clear();
|
|
|
|
if (m_zoomClamped <= FAR_THRESHOLD)
|
|
m_renderer->SetPerspectiveProjection(40.f, m_renderer->GetDisplayAspect(), 1.f, 300.f);
|
|
else
|
|
m_renderer->SetPerspectiveProjection(40.f, m_renderer->GetDisplayAspect(), 1.f, 600.f);
|
|
|
|
matrix4x4f modelview = matrix4x4f::Identity();
|
|
|
|
m_renderer->ClearScreen();
|
|
|
|
Graphics::Renderer::MatrixTicket ticket(m_renderer);
|
|
|
|
// units are lightyears, my friend
|
|
modelview.Translate(0.f, 0.f, -10.f - 10.f * m_zoom); // not zoomClamped, let us zoom out a bit beyond what we're drawing
|
|
modelview.Rotate(DEG2RAD(m_rotX), 1.f, 0.f, 0.f);
|
|
modelview.Rotate(DEG2RAD(m_rotZ), 0.f, 0.f, 1.f);
|
|
modelview.Translate(-FFRAC(m_pos.x) * Sector::SIZE, -FFRAC(m_pos.y) * Sector::SIZE, -FFRAC(m_pos.z) * Sector::SIZE);
|
|
m_renderer->SetTransform(modelview);
|
|
|
|
if (m_zoomClamped <= FAR_THRESHOLD)
|
|
DrawNearSectors(modelview);
|
|
else
|
|
DrawFarSectors(modelview);
|
|
|
|
m_renderer->SetTransform(matrix4x4f::Identity());
|
|
|
|
//draw star billboards in one go
|
|
m_renderer->SetAmbientColor(Color(30, 30, 30));
|
|
m_renderer->DrawTriangles(m_starVerts.get(), m_solidState, m_starMaterial.Get());
|
|
|
|
//draw sector legs in one go
|
|
if (!m_lineVerts->IsEmpty()) {
|
|
m_lines.SetData(m_lineVerts->GetNumVerts(), &m_lineVerts->position[0], &m_lineVerts->diffuse[0]);
|
|
m_lines.Draw(m_renderer, m_alphaBlendState);
|
|
}
|
|
|
|
if (!m_secLineVerts->IsEmpty()) {
|
|
m_sectorlines.SetData(m_secLineVerts->GetNumVerts(), &m_secLineVerts->position[0], &m_secLineVerts->diffuse[0]);
|
|
m_sectorlines.Draw(m_renderer, m_alphaBlendState);
|
|
}
|
|
|
|
// not quite the same as modelview
|
|
matrix4x4f trans = matrix4x4f::Identity();
|
|
trans.Translate(0.f, 0.f, -10.f - 10.f * m_zoom);
|
|
trans.Rotate(DEG2RAD(m_rotX), 1.f, 0.f, 0.f);
|
|
trans.Rotate(DEG2RAD(m_rotZ), 0.f, 0.f, 1.f);
|
|
trans.Translate(-(m_pos.x) * Sector::SIZE, -(m_pos.y) * Sector::SIZE, -(m_pos.z) * Sector::SIZE);
|
|
|
|
// calculate the player's location (it will be interpolated between systems during a hyperjump)
|
|
vector3f playerPos;
|
|
float currentStarSize;
|
|
GetPlayerPosAndStarSize(playerPos, currentStarSize);
|
|
|
|
// the next function assumes that m_renderer->(matrix4x4f::Identity()),
|
|
// so we run this function before we touch m_renderer again
|
|
if (m_drawRouteLines && !m_route.empty()) {
|
|
if (m_setupRouteLines) {
|
|
SetupRouteLines(playerPos);
|
|
}
|
|
DrawRouteLines(trans);
|
|
}
|
|
|
|
// player location indicator
|
|
// starting with the "trans" matrix, see above, adding translation to playerPos
|
|
matrix4x4f pTrans = trans * matrix4x4f::Translation(playerPos.x, playerPos.y, playerPos.z);
|
|
// rotate in the opposite direction, so that the disk always looks like a circle
|
|
pTrans.Rotate(DEG2RAD(-m_rotZ), 0, 0, 1);
|
|
pTrans.Rotate(DEG2RAD(-m_rotX), 1, 0, 0);
|
|
// corretion to the size of the current star, and hardcoded "3", the size of the player's indicator
|
|
pTrans.Scale(currentStarSize * 3.);
|
|
// move this disk 0.03 light years further so that it does not overlap the star, and selected indicator and hyperspace target indicator
|
|
m_renderer->SetTransform(matrix4x4f::Translation((pTrans * vector3f(.0f)).NormalizedSafe() * 0.03) * pTrans);
|
|
m_disk->SetColor(Color(0, 0, 204));
|
|
m_disk->Draw(m_renderer);
|
|
}
|
|
|
|
void SectorView::SetHyperspaceTarget(const SystemPath &path)
|
|
{
|
|
m_hyperspaceTarget = path;
|
|
onHyperspaceTargetChanged.emit();
|
|
}
|
|
|
|
void SectorView::ResetHyperspaceTarget()
|
|
{
|
|
SystemPath old = m_hyperspaceTarget;
|
|
m_hyperspaceTarget = Pi::game->GetSpace()->GetStarSystem()->GetStars()[0]->GetPath();
|
|
|
|
if (!old.IsSameSystem(m_hyperspaceTarget)) {
|
|
onHyperspaceTargetChanged.emit();
|
|
}
|
|
}
|
|
|
|
void SectorView::GotoSector(const SystemPath &path)
|
|
{
|
|
m_posMovingTo = vector3f(path.sectorX, path.sectorY, path.sectorZ);
|
|
|
|
// for performance don't animate the travel if we're Far Zoomed
|
|
if (m_zoomClamped > FAR_THRESHOLD) {
|
|
m_pos = m_posMovingTo;
|
|
}
|
|
}
|
|
|
|
void SectorView::GotoSystem(const SystemPath &path)
|
|
{
|
|
RefCountedPtr<Sector> ps = GetCached(path);
|
|
const vector3f &p = ps->m_systems[path.systemIndex].GetPosition();
|
|
m_posMovingTo.x = path.sectorX + p.x / Sector::SIZE;
|
|
m_posMovingTo.y = path.sectorY + p.y / Sector::SIZE;
|
|
m_posMovingTo.z = path.sectorZ + p.z / Sector::SIZE;
|
|
|
|
// for performance don't animate the travel if we're Far Zoomed
|
|
if (m_zoomClamped > FAR_THRESHOLD) {
|
|
m_pos = m_posMovingTo;
|
|
}
|
|
}
|
|
|
|
bool SectorView::IsCenteredOn(const SystemPath &path)
|
|
{
|
|
RefCountedPtr<Sector> ps = GetCached(path);
|
|
const vector3f &p = ps->m_systems[path.systemIndex].GetPosition();
|
|
vector3f diff = vector3f(
|
|
fabs(m_pos.x - path.sectorX - p.x / Sector::SIZE),
|
|
fabs(m_pos.y - path.sectorY - p.y / Sector::SIZE),
|
|
fabs(m_pos.z - path.sectorZ - p.z / Sector::SIZE));
|
|
return ((diff.x < 0.005f && diff.y < 0.005f && diff.z < 0.005f));
|
|
}
|
|
|
|
void SectorView::SetSelected(const SystemPath &path)
|
|
{
|
|
if (path.IsBodyPath())
|
|
m_selected = path;
|
|
else if (path.IsSystemPath()) {
|
|
RefCountedPtr<StarSystem> system = m_galaxy->GetStarSystem(path);
|
|
m_selected = CheckPathInRoute(system->GetStars()[0]->GetPath());
|
|
}
|
|
}
|
|
|
|
void SectorView::SwitchToPath(const SystemPath &path)
|
|
{
|
|
SetSelected(path);
|
|
if (m_automaticSystemSelection)
|
|
GotoSystem(path);
|
|
}
|
|
|
|
void SectorView::OnClickSystem(const SystemPath &path)
|
|
{
|
|
SwitchToPath(path);
|
|
}
|
|
|
|
// check the route, maybe this system is there, then we'll take the path from the route
|
|
const SystemPath &SectorView::CheckPathInRoute(const SystemPath &path)
|
|
{
|
|
for (const auto &p : m_route)
|
|
if (p.IsSameSystem(path))
|
|
return p;
|
|
return path;
|
|
}
|
|
|
|
void SectorView::PutSystemLabels(RefCountedPtr<Sector> sec, const vector3f &origin, int drawRadius)
|
|
{
|
|
PROFILE_SCOPED()
|
|
|
|
Uint32 sysIdx = 0;
|
|
for (std::vector<Sector::System>::iterator sys = sec->m_systems.begin(); sys != sec->m_systems.end(); ++sys, ++sysIdx) {
|
|
// skip the system if it doesn't fall within the sphere we're viewing.
|
|
if ((m_pos * Sector::SIZE - (*sys).GetFullPosition()).Length() > drawRadius) continue;
|
|
|
|
// if the system is the current system or target we can't skip it
|
|
bool can_skip = !sys->IsSameSystem(m_selected) && !sys->IsSameSystem(m_hyperspaceTarget) && !sys->IsSameSystem(m_current);
|
|
|
|
// skip if we have no population and won't drawn uninhabited systems
|
|
if (can_skip && (sys->GetPopulation() <= 0 && !m_drawUninhabitedLabels)) continue;
|
|
|
|
// skip the system if it belongs to a Faction we've toggled off and we can skip it
|
|
if (can_skip && m_hiddenFactions.find(sys->GetFaction()) != m_hiddenFactions.end()) continue;
|
|
|
|
// determine if system in hyperjump range or not
|
|
RefCountedPtr<const Sector> playerSec = GetCached(m_current);
|
|
float dist = Sector::DistanceBetween(sec, sysIdx, playerSec, m_current.systemIndex);
|
|
bool inRange = dist <= m_playerHyperspaceRange;
|
|
|
|
// skip if we're out of rangen and won't draw out of range systems systems
|
|
if (can_skip && (!inRange && !m_drawOutRangeLabels)) continue;
|
|
|
|
// place the label
|
|
vector3d systemPos = vector3d((*sys).GetFullPosition() - origin);
|
|
vector3d screenPos;
|
|
Gui::Screen::Project(systemPos, screenPos);
|
|
// reject back-projected labels (negative Z in clipspace is in front of the view plane)
|
|
if (screenPos.z < 0.0f) {
|
|
// work out the colour
|
|
Color labelColor = sys->GetFaction()->AdjustedColour(sys->GetPopulation(), inRange);
|
|
|
|
// get a system path to pass to the event handler when the label is licked
|
|
SystemPath sysPath = SystemPath((*sys).sx, (*sys).sy, (*sys).sz, sysIdx);
|
|
|
|
// label text
|
|
std::string text = "";
|
|
if (((inRange || m_drawOutRangeLabels) && (sys->GetPopulation() > 0 || m_drawUninhabitedLabels)) || !can_skip)
|
|
text = sys->GetName();
|
|
|
|
// setup the label;
|
|
m_clickableLabels->Add(text, sigc::bind(sigc::mem_fun(this, &SectorView::OnClickSystem), sysPath), screenPos.x, screenPos.y, labelColor);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SectorView::PutFactionLabels(const vector3f &origin)
|
|
{
|
|
PROFILE_SCOPED()
|
|
|
|
Gui::Screen::EnterOrtho();
|
|
|
|
if (!m_material)
|
|
m_material.Reset(m_renderer->CreateMaterial(Graphics::MaterialDescriptor()));
|
|
|
|
static const Color labelBorder(13, 13, 31, 166);
|
|
const auto renderState = Gui::Screen::alphaBlendState;
|
|
|
|
for (auto it = m_visibleFactions.begin(); it != m_visibleFactions.end(); ++it) {
|
|
if ((*it)->hasHomeworld && m_hiddenFactions.find((*it)) == m_hiddenFactions.end()) {
|
|
Sector::System sys = GetCached((*it)->homeworld)->m_systems[(*it)->homeworld.systemIndex];
|
|
if ((m_pos * Sector::SIZE - sys.GetFullPosition()).Length() > (m_zoomClamped / FAR_THRESHOLD) * OUTER_RADIUS) continue;
|
|
|
|
vector3d pos;
|
|
Gui::Screen::Project(vector3d(sys.GetFullPosition() - origin), pos);
|
|
if (pos.z < 0.0f) { // reject back-projected stars
|
|
|
|
std::string labelText = sys.GetName() + "\n" + (*it)->name;
|
|
Color labelColor = (*it)->colour;
|
|
float labelHeight = 0;
|
|
float labelWidth = 0;
|
|
|
|
Gui::Screen::MeasureString(labelText, labelWidth, labelHeight);
|
|
|
|
// draw a big diamond for the location of the star
|
|
static const float STARSIZE = 5;
|
|
Graphics::VertexArray outline(Graphics::ATTRIB_POSITION);
|
|
outline.Add(vector3f(pos.x - STARSIZE - 1.f, pos.y, 1));
|
|
outline.Add(vector3f(pos.x, pos.y + STARSIZE + 1.f, 1));
|
|
outline.Add(vector3f(pos.x, pos.y - STARSIZE - 1.f, 1));
|
|
outline.Add(vector3f(pos.x + STARSIZE + 1.f, pos.y, 1));
|
|
m_material->diffuse = { 0, 0, 0, 255 };
|
|
m_renderer->DrawTriangles(&outline, renderState, m_material.Get(), Graphics::TRIANGLE_STRIP);
|
|
|
|
Graphics::VertexArray marker(Graphics::ATTRIB_POSITION);
|
|
marker.Add(vector3f(pos.x - STARSIZE, pos.y, 0));
|
|
marker.Add(vector3f(pos.x, pos.y + STARSIZE, 0));
|
|
marker.Add(vector3f(pos.x, pos.y - STARSIZE, 0));
|
|
marker.Add(vector3f(pos.x + STARSIZE, pos.y, 0));
|
|
m_material->diffuse = labelColor;
|
|
m_renderer->DrawTriangles(&marker, renderState, m_material.Get(), Graphics::TRIANGLE_STRIP);
|
|
|
|
// draw a surface for the label to sit on
|
|
static const float MARGINLEFT = 8;
|
|
float halfheight = labelHeight / 2.0;
|
|
Graphics::VertexArray surface(Graphics::ATTRIB_POSITION);
|
|
surface.Add(vector3f(pos.x + MARGINLEFT - 2.f, pos.y - halfheight, 0));
|
|
surface.Add(vector3f(pos.x + MARGINLEFT - 2.f, pos.y + halfheight, 0));
|
|
surface.Add(vector3f(pos.x + MARGINLEFT + labelWidth + 2.f, pos.y - halfheight, 0));
|
|
surface.Add(vector3f(pos.x + MARGINLEFT + labelWidth + 2.f, pos.y + halfheight, 0));
|
|
m_material->diffuse = labelBorder;
|
|
m_renderer->DrawTriangles(&surface, renderState, m_material.Get(), Graphics::TRIANGLE_STRIP);
|
|
|
|
if (labelColor.GetLuminance() > 204)
|
|
labelColor.a = 204; // luminance is sometimes a bit overly
|
|
m_clickableLabels->Add(labelText, sigc::bind(sigc::mem_fun(this, &SectorView::OnClickSystem), (*it)->homeworld), pos.x + MARGINLEFT, pos.y - (halfheight / 2.0) - 1.f, labelColor);
|
|
}
|
|
}
|
|
}
|
|
Gui::Screen::LeaveOrtho();
|
|
}
|
|
|
|
void SectorView::AddStarBillboard(const matrix4x4f &trans, const vector3f &pos, const Color &col, float size)
|
|
{
|
|
const matrix3x3f rot = trans.GetOrient().Transpose();
|
|
|
|
const vector3f offset = trans * pos;
|
|
|
|
const vector3f rotv1 = rot * vector3f(size / 2.f, -size / 2.f, 0.0f);
|
|
const vector3f rotv2 = rot * vector3f(size / 2.f, size / 2.f, 0.0f);
|
|
|
|
Graphics::VertexArray &va = *m_starVerts;
|
|
va.Add(offset - rotv1, col, vector2f(0.f, 0.f)); //top left
|
|
va.Add(offset - rotv2, col, vector2f(0.f, 1.f)); //bottom left
|
|
va.Add(offset + rotv2, col, vector2f(1.f, 0.f)); //top right
|
|
|
|
va.Add(offset + rotv2, col, vector2f(1.f, 0.f)); //top right
|
|
va.Add(offset - rotv2, col, vector2f(0.f, 1.f)); //bottom left
|
|
va.Add(offset + rotv1, col, vector2f(1.f, 1.f)); //bottom right
|
|
}
|
|
|
|
void SectorView::OnToggleFaction(Gui::ToggleButton *button, bool pressed, const Faction *faction)
|
|
{
|
|
// hide or show the faction's systems depending on whether the button is pressed
|
|
if (pressed)
|
|
m_hiddenFactions.erase(faction);
|
|
else
|
|
m_hiddenFactions.insert(faction);
|
|
|
|
m_toggledFaction = true;
|
|
}
|
|
|
|
void SectorView::DrawNearSectors(const matrix4x4f &modelview)
|
|
{
|
|
PROFILE_SCOPED()
|
|
m_visibleFactions.clear();
|
|
|
|
for (int sx = -DRAW_RAD; sx <= DRAW_RAD; sx++) {
|
|
for (int sy = -DRAW_RAD; sy <= DRAW_RAD; sy++) {
|
|
for (int sz = -DRAW_RAD; sz <= DRAW_RAD; sz++) {
|
|
DrawNearSector(int(floorf(m_pos.x)) + sx, int(floorf(m_pos.y)) + sy, int(floorf(m_pos.z)) + sz,
|
|
modelview * matrix4x4f::Translation(Sector::SIZE * sx, Sector::SIZE * sy, Sector::SIZE * sz));
|
|
}
|
|
}
|
|
}
|
|
|
|
// ...then switch and do all the labels
|
|
const vector3f secOrigin = vector3f(int(floorf(m_pos.x)), int(floorf(m_pos.y)), int(floorf(m_pos.z)));
|
|
|
|
m_renderer->SetTransform(modelview);
|
|
Gui::Screen::EnterOrtho();
|
|
for (int sx = -DRAW_RAD; sx <= DRAW_RAD; sx++) {
|
|
for (int sy = -DRAW_RAD; sy <= DRAW_RAD; sy++) {
|
|
for (int sz = -DRAW_RAD; sz <= DRAW_RAD; sz++) {
|
|
PutSystemLabels(GetCached(SystemPath(sx + secOrigin.x, sy + secOrigin.y, sz + secOrigin.z)), Sector::SIZE * secOrigin, Sector::SIZE * DRAW_RAD);
|
|
}
|
|
}
|
|
}
|
|
Gui::Screen::LeaveOrtho();
|
|
}
|
|
|
|
bool SectorView::MoveRouteItemUp(const std::vector<SystemPath>::size_type element)
|
|
{
|
|
if (element == 0 || element >= m_route.size()) return false;
|
|
|
|
std::swap(m_route[element - 1], m_route[element]);
|
|
|
|
m_setupRouteLines = true;
|
|
return true;
|
|
}
|
|
|
|
bool SectorView::MoveRouteItemDown(const std::vector<SystemPath>::size_type element)
|
|
{
|
|
if (element >= m_route.size() - 1) return false;
|
|
|
|
std::swap(m_route[element + 1], m_route[element]);
|
|
|
|
m_setupRouteLines = true;
|
|
return true;
|
|
}
|
|
|
|
void SectorView::UpdateRouteItem(const std::vector<SystemPath>::size_type element, const SystemPath &path)
|
|
{
|
|
m_route[element] = path;
|
|
m_setupRouteLines = true;
|
|
}
|
|
|
|
void SectorView::AddToRoute(const SystemPath &path)
|
|
{
|
|
m_route.push_back(path);
|
|
m_setupRouteLines = true;
|
|
}
|
|
|
|
bool SectorView::RemoveRouteItem(const std::vector<SystemPath>::size_type element)
|
|
{
|
|
if (element < m_route.size()) {
|
|
m_route.erase(m_route.begin() + element);
|
|
m_setupRouteLines = true;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void SectorView::ClearRoute()
|
|
{
|
|
m_route.clear();
|
|
m_setupRouteLines = true;
|
|
}
|
|
|
|
std::vector<SystemPath> SectorView::GetRoute()
|
|
{
|
|
return m_route;
|
|
}
|
|
|
|
const std::string SectorView::AutoRoute(const SystemPath &start, const SystemPath &target, std::vector<SystemPath> &outRoute) const
|
|
{
|
|
const RefCountedPtr<const Sector> start_sec = m_galaxy->GetSector(start);
|
|
const RefCountedPtr<const Sector> target_sec = m_galaxy->GetSector(target);
|
|
|
|
LuaRef try_hdrive = LuaObject<Player>::CallMethod<LuaRef>(Pi::player, "GetEquip", "engine", 1);
|
|
if (try_hdrive.IsNil())
|
|
return "NO_DRIVE";
|
|
// Get the player's hyperdrive from Lua, later used to calculate the duration between systems
|
|
const ScopedTable hyperdrive = ScopedTable(try_hdrive);
|
|
// Cache max range so it doesn't get recalculated every time we call GetDuration
|
|
const float max_range = hyperdrive.CallMethod<float>("GetMaximumRange", Pi::player);
|
|
const float max_range_sqr = max_range * max_range;
|
|
|
|
// use the square of the distance to avoid doing a sqrt for each sector
|
|
const float distSqr = Sector::DistanceBetweenSqr(start_sec, start.systemIndex, target_sec, target.systemIndex) * 1.10;
|
|
|
|
// the maximum distance that anything can be from the direct line between the start and target systems
|
|
const float max_dist_from_straight_line = (Sector::SIZE * 3);
|
|
|
|
// nodes[0] is always start
|
|
std::vector<SystemPath> nodes;
|
|
{
|
|
// calculate an approximate initial number of nodes
|
|
const float dist = sqrt(distSqr);
|
|
// +2 for the sectors near start and target, *9 for the sectors immediately around the path... might want to be bigger but seems to work
|
|
const size_t num_sectors_covered = (size_t(dist / Sector::SIZE) + 2) * 9;
|
|
const size_t num_systems_per_sector = 6; // total guess
|
|
nodes.reserve(num_sectors_covered * num_systems_per_sector);
|
|
}
|
|
nodes.push_back(start);
|
|
|
|
const Sint32 minX = std::min(start.sectorX, target.sectorX) - 2, maxX = std::max(start.sectorX, target.sectorX) + 2;
|
|
const Sint32 minY = std::min(start.sectorY, target.sectorY) - 2, maxY = std::max(start.sectorY, target.sectorY) + 2;
|
|
const Sint32 minZ = std::min(start.sectorZ, target.sectorZ) - 2, maxZ = std::max(start.sectorZ, target.sectorZ) + 2;
|
|
const vector3f start_pos = start_sec->m_systems[start.systemIndex].GetFullPosition();
|
|
const vector3f target_pos = target_sec->m_systems[target.systemIndex].GetFullPosition();
|
|
|
|
// go sector by sector for the minimum cube of sectors and add systems
|
|
// if they are within 110% of dist of both start and target
|
|
size_t secLineToFar = 0u;
|
|
for (Sint32 sx = minX; sx <= maxX; sx++) {
|
|
for (Sint32 sy = minY; sy <= maxY; sy++) {
|
|
for (Sint32 sz = minZ; sz < maxZ; sz++) {
|
|
const SystemPath sec_path = SystemPath(sx, sy, sz);
|
|
|
|
// early out here if the sector is too far from the direct line
|
|
vector3f sec_centre(Sector::SIZE * vector3f(float(sx) + 0.5f, float(sy) + 0.5f, float(sz) + 0.5f));
|
|
const float secLineDist = MathUtil::DistanceFromLine(start_pos, target_pos, sec_centre);
|
|
// this test is deliberately conservative to over include sectors, a better test might use the nearest corner of an AABB to the line - Math hard, Andy tired
|
|
if ((secLineDist - Sector::SIZE) > max_dist_from_straight_line) {
|
|
++secLineToFar;
|
|
continue;
|
|
}
|
|
|
|
// GetSector is very expensive if it's not in the cache
|
|
RefCountedPtr<const Sector> sec = m_galaxy->GetSector(sec_path);
|
|
for (std::vector<Sector::System>::size_type s = 0; s < sec->m_systems.size(); s++) {
|
|
if (start.IsSameSystem(sec->m_systems[s].GetPath()))
|
|
continue; // start is already nodes[0]
|
|
|
|
const float lineDist = MathUtil::DistanceFromLine(start_pos, target_pos, sec->m_systems[s].GetFullPosition());
|
|
|
|
if (Sector::DistanceBetweenSqr(start_sec, start.systemIndex, sec, sec->m_systems[s].idx) <= distSqr &&
|
|
Sector::DistanceBetweenSqr(target_sec, target.systemIndex, sec, sec->m_systems[s].idx) <= distSqr &&
|
|
lineDist < max_dist_from_straight_line) {
|
|
nodes.push_back(sec->m_systems[s].GetPath());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Output("SectorView::AutoRoute, nodes to search = %lu, earlied out sector distance from line: %lu times.\n", nodes.size(), secLineToFar);
|
|
|
|
// setup inital values and set everything as unvisited
|
|
std::vector<float> path_dist; // distance from source to node
|
|
std::vector<std::vector<SystemPath>::size_type> path_prev; // previous node in optimal path
|
|
std::unordered_set<std::vector<SystemPath>::size_type> unvisited;
|
|
// we know how big these need to be, to reserve space up front
|
|
path_dist.reserve(nodes.size());
|
|
path_prev.reserve(nodes.size());
|
|
unvisited.reserve(nodes.size());
|
|
// initialise them
|
|
for (std::vector<SystemPath>::size_type i = 0; i < nodes.size(); i++) {
|
|
path_dist.push_back(INFINITY);
|
|
path_prev.push_back(0);
|
|
unvisited.insert(i);
|
|
}
|
|
|
|
// distance to the start is 0
|
|
path_dist[0] = 0.f;
|
|
|
|
size_t totalSkipped = 0u;
|
|
while (unvisited.size() > 0) {
|
|
// find the closest node (for the first loop this will be start)
|
|
std::vector<SystemPath>::size_type closest_i = *unvisited.begin();
|
|
for (auto it : unvisited) {
|
|
if (path_dist[it] < path_dist[closest_i])
|
|
closest_i = it;
|
|
}
|
|
|
|
// mark it as visited
|
|
unvisited.erase(closest_i);
|
|
|
|
// if this is the target then we have found the route
|
|
const SystemPath &closest = nodes[closest_i];
|
|
if (closest.IsSameSystem(target))
|
|
break;
|
|
|
|
RefCountedPtr<const Sector> closest_sec = m_galaxy->GetSector(closest);
|
|
|
|
// if not, loop through all unvisited nodes
|
|
// since every system is technically reachable from every other system
|
|
// everything is a neighbor :)
|
|
for (auto it : unvisited) {
|
|
const SystemPath &v = nodes[it];
|
|
// everything is a neighbor isn't quite true as the ship has a max_range for each jump!
|
|
if ((SystemPath::SectorDistanceSqr(closest, v) * Sector::SIZE) > max_range_sqr) {
|
|
++totalSkipped;
|
|
continue;
|
|
}
|
|
|
|
RefCountedPtr<const Sector> v_sec = m_galaxy->GetSector(v); // this causes it to generate a sector (slooooooow)
|
|
|
|
const float v_dist_ly = Sector::DistanceBetween(closest_sec, closest.systemIndex, v_sec, v.systemIndex);
|
|
|
|
// in this case, duration is used for the distance since that's what we are optimizing
|
|
float v_dist = hyperdrive.CallMethod<float>("GetDuration", Pi::player, v_dist_ly, max_range);
|
|
|
|
v_dist += path_dist[closest_i]; // we want the total duration from start to this node
|
|
if (v_dist < path_dist[it]) {
|
|
// if our calculated duration is less than a previous value, this path is more efficent
|
|
// so store/override it
|
|
path_dist[it] = v_dist;
|
|
path_prev[it] = closest_i;
|
|
}
|
|
}
|
|
}
|
|
Output("SectorView::AutoRoute, total times that nodes were skipped = %lu\n", totalSkipped);
|
|
|
|
bool foundRoute = false;
|
|
std::vector<SystemPath>::size_type u = 0;
|
|
|
|
// find the index of our target
|
|
for (std::vector<SystemPath>::size_type i = 0, numNodes = nodes.size(); i < numNodes; i++) {
|
|
if (target.IsSameSystem(nodes[i])) {
|
|
u = i;
|
|
foundRoute = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// It's posible that there is no valid route
|
|
if (foundRoute) {
|
|
outRoute.reserve(nodes.size());
|
|
// Build the route, in reverse starting with the target
|
|
while (u != 0) {
|
|
outRoute.push_back(m_galaxy->GetStarSystem(nodes[u])->GetStars()[0]->GetPath());
|
|
u = path_prev[u];
|
|
}
|
|
std::reverse(std::begin(outRoute), std::end(outRoute));
|
|
return "OKAY";
|
|
}
|
|
return "NO_VALID_ROUTE";
|
|
}
|
|
|
|
void SectorView::DrawRouteLines(const matrix4x4f &trans)
|
|
{
|
|
if (m_route.empty())
|
|
return;
|
|
|
|
m_renderer->SetTransform(trans);
|
|
m_routeLines.Draw(m_renderer, m_alphaBlendState);
|
|
}
|
|
|
|
void SectorView::SetupRouteLines(const vector3f &playerAbsPos)
|
|
{
|
|
assert(!m_route.empty());
|
|
assert(m_setupRouteLines);
|
|
m_setupRouteLines = false;
|
|
|
|
std::unique_ptr<Graphics::VertexArray> verts;
|
|
verts.reset(new Graphics::VertexArray(Graphics::ATTRIB_POSITION, m_route.size() * 2));
|
|
verts->Clear();
|
|
|
|
vector3f startPos = playerAbsPos;
|
|
for (std::vector<SystemPath>::size_type i = 0; i < m_route.size(); ++i) {
|
|
RefCountedPtr<const Sector> jumpSec = m_galaxy->GetSector(m_route[i]);
|
|
const Sector::System &jumpSecSys = jumpSec->m_systems[m_route[i].systemIndex];
|
|
const vector3f jumpAbsPos = Sector::SIZE * vector3f(float(jumpSec->sx), float(jumpSec->sy), float(jumpSec->sz)) + jumpSecSys.GetPosition();
|
|
|
|
if (i > 0) {
|
|
RefCountedPtr<const Sector> prevSec = m_galaxy->GetSector(m_route[i - 1]);
|
|
const Sector::System &prevSecSys = prevSec->m_systems[m_route[i - 1].systemIndex];
|
|
const vector3f prevAbsPos = Sector::SIZE * vector3f(float(prevSec->sx), float(prevSec->sy), float(prevSec->sz)) + prevSecSys.GetPosition();
|
|
startPos = prevAbsPos;
|
|
}
|
|
|
|
verts->Add(startPos, Color(20, 20, 0, 127));
|
|
verts->Add(jumpAbsPos, Color(255, 255, 0, 255));
|
|
}
|
|
|
|
m_routeLines.SetData(verts->GetNumVerts(), &verts->position[0], &verts->diffuse[0]);
|
|
}
|
|
|
|
void SectorView::GetPlayerPosAndStarSize(vector3f &playerPosOut, float ¤tStarSizeOut)
|
|
{
|
|
// calculate the player's location (it will be interpolated between systems during a hyperjump)
|
|
const Sector::System currentSystem = GetCached(m_current)->m_systems[m_current.systemIndex];
|
|
playerPosOut = Sector::SIZE * vector3f(float(m_current.sectorX), float(m_current.sectorY), float(m_current.sectorZ)) + currentSystem.GetPosition();
|
|
currentStarSizeOut = StarSystem::starScale[currentSystem.GetStarType(0)];
|
|
if (!m_inSystem) {
|
|
// we are in hyperspace, interpolate
|
|
// m_current has been set to the destination path
|
|
// destination system data:
|
|
const SystemPath dstPath = Pi::game->GetHyperspaceDest();
|
|
const Sector::System dstSystem = GetCached(dstPath)->m_systems[dstPath.systemIndex];
|
|
const vector3f dstPos = Sector::SIZE * vector3f(float(dstPath.sectorX), float(dstPath.sectorY), float(dstPath.sectorZ)) + dstSystem.GetPosition();
|
|
// jumpProcess: 0.0f (at source) .. 1.0f (at destination)
|
|
// smoothing speed at the ends with cubic interpolation
|
|
const float jumpProcess = AnimationCurves::InOutCubicEasing(Pi::game->GetHyperspaceProgress() / Pi::game->GetHyperspaceDuration());
|
|
// player location indicator's size of the star, on which it is drawn
|
|
// so we interpolate it too
|
|
const float dstStarSize = StarSystem::starScale[dstSystem.GetStarType(0)];
|
|
// interpolating player location indicator's position and size
|
|
playerPosOut = playerPosOut.Lerp(dstPos, jumpProcess);
|
|
currentStarSizeOut = MathUtil::Lerp(currentStarSizeOut, dstStarSize, jumpProcess);
|
|
}
|
|
}
|
|
|
|
void SectorView::DrawNearSector(const int sx, const int sy, const int sz, const matrix4x4f &trans)
|
|
{
|
|
PROFILE_SCOPED()
|
|
m_renderer->SetTransform(trans);
|
|
RefCountedPtr<Sector> ps = GetCached(SystemPath(sx, sy, sz));
|
|
|
|
const int cz = int(floor(m_pos.z + 0.5f));
|
|
|
|
if (cz == sz) {
|
|
static const Color darkgreen(0, 51, 0, 255);
|
|
const vector3f vts[] = {
|
|
trans * vector3f(0.f, 0.f, 0.f),
|
|
trans * vector3f(0.f, Sector::SIZE, 0.f),
|
|
trans * vector3f(Sector::SIZE, Sector::SIZE, 0.f),
|
|
trans * vector3f(Sector::SIZE, 0.f, 0.f)
|
|
};
|
|
|
|
// reserve some more space
|
|
const size_t newNum = m_secLineVerts->GetNumVerts() + 8;
|
|
m_secLineVerts->position.reserve(newNum);
|
|
m_secLineVerts->diffuse.reserve(newNum);
|
|
|
|
m_secLineVerts->Add(vts[0], darkgreen); // line segment 1
|
|
m_secLineVerts->Add(vts[1], darkgreen);
|
|
m_secLineVerts->Add(vts[1], darkgreen); // line segment 2
|
|
m_secLineVerts->Add(vts[2], darkgreen);
|
|
m_secLineVerts->Add(vts[2], darkgreen); // line segment 3
|
|
m_secLineVerts->Add(vts[3], darkgreen);
|
|
m_secLineVerts->Add(vts[3], darkgreen); // line segment 4
|
|
m_secLineVerts->Add(vts[0], darkgreen);
|
|
}
|
|
|
|
const size_t numLineVerts = ps->m_systems.size() * 8;
|
|
m_lineVerts->position.reserve(numLineVerts);
|
|
m_lineVerts->diffuse.reserve(numLineVerts);
|
|
|
|
Uint32 sysIdx = 0;
|
|
for (std::vector<Sector::System>::iterator i = ps->m_systems.begin(); i != ps->m_systems.end(); ++i, ++sysIdx) {
|
|
// calculate where the system is in relation the centre of the view...
|
|
const vector3f sysAbsPos = Sector::SIZE * vector3f(float(sx), float(sy), float(sz)) + i->GetPosition();
|
|
const vector3f toCentreOfView = m_pos * Sector::SIZE - sysAbsPos;
|
|
|
|
// ...and skip the system if it doesn't fall within the sphere we're viewing.
|
|
if (toCentreOfView.Length() > OUTER_RADIUS) continue;
|
|
|
|
const bool bIsCurrentSystem = i->IsSameSystem(m_current);
|
|
const bool bInRoute = std::find_if(m_route.begin(), m_route.end(), [i](const SystemPath &a) -> bool { return i->IsSameSystem(a); }) != m_route.end();
|
|
|
|
// if the system is the current system or target we can't skip it
|
|
bool can_skip = !i->IsSameSystem(m_selected) && !i->IsSameSystem(m_hyperspaceTarget) && !bIsCurrentSystem && !bInRoute;
|
|
|
|
// if the system belongs to a faction we've chosen to temporarily hide
|
|
// then skip it if we can
|
|
m_visibleFactions.insert(i->GetFaction());
|
|
if (can_skip && m_hiddenFactions.find(i->GetFaction()) != m_hiddenFactions.end()) continue;
|
|
|
|
// determine if system in hyperjump range or not
|
|
RefCountedPtr<const Sector> playerSec = GetCached(m_current);
|
|
float dist = Sector::DistanceBetween(ps, sysIdx, playerSec, m_current.systemIndex);
|
|
bool inRange = dist <= m_playerHyperspaceRange;
|
|
|
|
// don't worry about looking for inhabited systems if they're
|
|
// unexplored (same calculation as in StarSystem.cpp) or we've
|
|
// already retrieved their population.
|
|
if (i->GetPopulation() < 0 && isqrt(1 + sx * sx + sy * sy + sz * sz) <= 90) {
|
|
|
|
// only do this once we've pretty much stopped moving.
|
|
vector3f diff = vector3f(
|
|
fabs(m_posMovingTo.x - m_pos.x),
|
|
fabs(m_posMovingTo.y - m_pos.y),
|
|
fabs(m_posMovingTo.z - m_pos.z));
|
|
|
|
// Ideally, since this takes so f'ing long, it wants to be done as a threaded job but haven't written that yet.
|
|
if ((diff.x < 0.001f && diff.y < 0.001f && diff.z < 0.001f)) {
|
|
SystemPath current = SystemPath(sx, sy, sz, sysIdx);
|
|
RefCountedPtr<StarSystem> pSS = m_galaxy->GetStarSystem(current);
|
|
i->SetPopulation(pSS->GetTotalPop());
|
|
}
|
|
}
|
|
|
|
matrix4x4f systrans = trans * matrix4x4f::Translation(i->GetPosition().x, i->GetPosition().y, i->GetPosition().z);
|
|
m_renderer->SetTransform(systrans);
|
|
|
|
// for out-of-range systems draw leg only if we draw label
|
|
if ((m_drawVerticalLines && (inRange || m_drawOutRangeLabels) && (i->GetPopulation() > 0 || m_drawUninhabitedLabels)) || !can_skip) {
|
|
|
|
const Color light(128, 128, 128);
|
|
const Color dark(51, 51, 51);
|
|
|
|
// draw system "leg"
|
|
float z = -i->GetPosition().z;
|
|
if (sz <= cz)
|
|
z = z + abs(cz - sz) * Sector::SIZE;
|
|
else
|
|
z = z - abs(cz - sz) * Sector::SIZE;
|
|
m_lineVerts->Add(systrans * vector3f(0.f, 0.f, z), light);
|
|
m_lineVerts->Add(systrans * vector3f(0.f, 0.f, z * 0.5f), dark);
|
|
m_lineVerts->Add(systrans * vector3f(0.f, 0.f, z * 0.5f), dark);
|
|
m_lineVerts->Add(systrans * vector3f(0.f, 0.f, 0.f), light);
|
|
|
|
//cross at other end
|
|
m_lineVerts->Add(systrans * vector3f(-0.1f, -0.1f, z), light);
|
|
m_lineVerts->Add(systrans * vector3f(0.1f, 0.1f, z), light);
|
|
m_lineVerts->Add(systrans * vector3f(-0.1f, 0.1f, z), light);
|
|
m_lineVerts->Add(systrans * vector3f(0.1f, -0.1f, z), light);
|
|
}
|
|
|
|
if (i->IsSameSystem(m_selected)) {
|
|
if (m_selected != m_current && !bInRoute) {
|
|
const vector3f playerAbsPos = Sector::SIZE * vector3f(float(m_current.sectorX), float(m_current.sectorY), float(m_current.sectorZ)) +
|
|
GetCached(m_current)->m_systems[m_current.systemIndex].GetPosition();
|
|
m_selectedLine.SetStart(vector3f(0.f, 0.f, 0.f));
|
|
m_selectedLine.SetEnd(playerAbsPos - sysAbsPos);
|
|
m_selectedLine.Draw(m_renderer, m_solidState);
|
|
}
|
|
|
|
if (m_route.size() > 0) {
|
|
const SystemPath &routeBack = m_route.back();
|
|
if (!bInRoute) {
|
|
RefCountedPtr<Sector> backSec = GetCached(routeBack);
|
|
const vector3f hyperAbsPos =
|
|
Sector::SIZE * vector3f(routeBack.sectorX, routeBack.sectorY, routeBack.sectorZ) + backSec->m_systems[routeBack.systemIndex].GetPosition();
|
|
if (m_selected != m_current) {
|
|
m_secondLine.SetStart(vector3f(0.f, 0.f, 0.f));
|
|
m_secondLine.SetEnd(hyperAbsPos - sysAbsPos);
|
|
m_secondLine.Draw(m_renderer, m_solidState);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// draw star blob itself
|
|
systrans.Rotate(DEG2RAD(-m_rotZ), 0, 0, 1);
|
|
systrans.Rotate(DEG2RAD(-m_rotX), 1, 0, 0);
|
|
systrans.Scale((StarSystem::starScale[(*i).GetStarType(0)]));
|
|
m_renderer->SetTransform(systrans);
|
|
|
|
const Uint8 *col = StarSystem::starColors[(*i).GetStarType(0)];
|
|
AddStarBillboard(systrans, vector3f(0.f), Color(col[0], col[1], col[2], 255), 0.5f);
|
|
|
|
// selected indicator
|
|
if (i->IsSameSystem(m_selected)) {
|
|
m_disk->SetColor(Color(0, 204, 0));
|
|
// move this disk 0.01 light years further so that it does not overlap the star
|
|
m_renderer->SetTransform(matrix4x4f::Translation((systrans * vector3f(.0f)).NormalizedSafe() * 0.01) * systrans * matrix4x4f::ScaleMatrix(2.f));
|
|
m_disk->Draw(m_renderer);
|
|
}
|
|
// hyperspace target indicator (if different from selection)
|
|
if (i->IsSameSystem(m_hyperspaceTarget) && m_hyperspaceTarget != m_selected && (!m_inSystem || m_hyperspaceTarget != m_current)) {
|
|
m_disk->SetColor(Color(77, 77, 77));
|
|
// move this disk 0.02 light years further so that it does not overlap the star, and selected indicator
|
|
m_renderer->SetTransform(matrix4x4f::Translation((systrans * vector3f(.0f)).NormalizedSafe() * 0.02) * systrans * matrix4x4f::ScaleMatrix(2.f));
|
|
m_disk->Draw(m_renderer);
|
|
}
|
|
// hyperspace range sphere
|
|
if (bIsCurrentSystem && m_jumpSphere && m_playerHyperspaceRange > 0.0f) {
|
|
const matrix4x4f sphTrans = trans * matrix4x4f::Translation(i->GetPosition().x, i->GetPosition().y, i->GetPosition().z);
|
|
m_renderer->SetTransform(sphTrans * matrix4x4f::ScaleMatrix(m_playerHyperspaceRange));
|
|
m_jumpSphere->Draw(m_renderer);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SectorView::DrawFarSectors(const matrix4x4f &modelview)
|
|
{
|
|
PROFILE_SCOPED()
|
|
int buildRadius = ceilf((m_zoomClamped / FAR_THRESHOLD) * 3);
|
|
if (buildRadius <= DRAW_RAD) buildRadius = DRAW_RAD;
|
|
|
|
const vector3f secOrigin = vector3f(int(floorf(m_pos.x)), int(floorf(m_pos.y)), int(floorf(m_pos.z)));
|
|
|
|
// build vertex and colour arrays for all the stars we want to see, if we don't already have them
|
|
if (m_toggledFaction || buildRadius != m_radiusFar || !secOrigin.ExactlyEqual(m_secPosFar)) {
|
|
m_farstars.clear();
|
|
m_farstarsColor.clear();
|
|
m_visibleFactions.clear();
|
|
|
|
for (int sx = secOrigin.x - buildRadius; sx <= secOrigin.x + buildRadius; sx++) {
|
|
for (int sy = secOrigin.y - buildRadius; sy <= secOrigin.y + buildRadius; sy++) {
|
|
for (int sz = secOrigin.z - buildRadius; sz <= secOrigin.z + buildRadius; sz++) {
|
|
if ((vector3f(sx, sy, sz) - secOrigin).Length() <= buildRadius) {
|
|
BuildFarSector(GetCached(SystemPath(sx, sy, sz)), Sector::SIZE * secOrigin, m_farstars, m_farstarsColor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
m_secPosFar = secOrigin;
|
|
m_radiusFar = buildRadius;
|
|
m_toggledFaction = false;
|
|
}
|
|
|
|
// always draw the stars, slightly altering their size for different different resolutions, so they still look okay
|
|
if (m_farstars.size() > 0) {
|
|
m_farstarsPoints.SetData(m_renderer, m_farstars.size(), &m_farstars[0], &m_farstarsColor[0], modelview, 0.25f * (Graphics::GetScreenHeight() / 720.f));
|
|
m_farstarsPoints.Draw(m_renderer, m_alphaBlendState);
|
|
}
|
|
|
|
// also add labels for any faction homeworlds among the systems we've drawn
|
|
PutFactionLabels(Sector::SIZE * secOrigin);
|
|
}
|
|
|
|
void SectorView::BuildFarSector(RefCountedPtr<Sector> sec, const vector3f &origin, std::vector<vector3f> &points, std::vector<Color> &colors)
|
|
{
|
|
PROFILE_SCOPED()
|
|
Color starColor;
|
|
for (std::vector<Sector::System>::iterator i = sec->m_systems.begin(); i != sec->m_systems.end(); ++i) {
|
|
// skip the system if it doesn't fall within the sphere we're viewing.
|
|
if ((m_pos * Sector::SIZE - (*i).GetFullPosition()).Length() > (m_zoomClamped / FAR_THRESHOLD) * OUTER_RADIUS) continue;
|
|
|
|
if (!i->IsExplored()) {
|
|
points.push_back((*i).GetFullPosition() - origin);
|
|
colors.push_back({ 100, 100, 100, 155 }); // flat gray for unexplored systems
|
|
continue;
|
|
}
|
|
|
|
// if the system belongs to a faction we've chosen to hide also skip it, if it's not selectd in some way
|
|
m_visibleFactions.insert(i->GetFaction());
|
|
if (m_hiddenFactions.find(i->GetFaction()) != m_hiddenFactions.end() && !i->IsSameSystem(m_selected) && !i->IsSameSystem(m_hyperspaceTarget) && !i->IsSameSystem(m_current)) continue;
|
|
|
|
// otherwise add the system's position (origin must be m_pos's *sector* or we get judder)
|
|
// and faction color to the list to draw
|
|
starColor = i->GetFaction()->colour;
|
|
starColor.a = 120;
|
|
|
|
points.push_back((*i).GetFullPosition() - origin);
|
|
colors.push_back(starColor);
|
|
}
|
|
}
|
|
|
|
void SectorView::OnSwitchTo()
|
|
{
|
|
m_renderer->SetViewport({ 0, 0, Graphics::GetScreenWidth(), Graphics::GetScreenHeight() });
|
|
Pi::input->AddInputFrame(&InputBindings);
|
|
Update();
|
|
}
|
|
|
|
void SectorView::OnSwitchFrom()
|
|
{
|
|
Pi::input->RemoveInputFrame(&InputBindings);
|
|
}
|
|
|
|
void SectorView::Update()
|
|
{
|
|
PROFILE_SCOPED()
|
|
SystemPath last_current = m_current;
|
|
|
|
if (Pi::game->IsNormalSpace()) {
|
|
m_inSystem = true;
|
|
m_current = Pi::game->GetSpace()->GetStarSystem()->GetPath();
|
|
} else {
|
|
m_inSystem = false;
|
|
m_current = Pi::game->GetHyperspaceSource();
|
|
}
|
|
|
|
const float frameTime = Pi::GetFrameTime();
|
|
|
|
matrix4x4f rot = matrix4x4f::Identity();
|
|
rot.RotateX(DEG2RAD(-m_rotX));
|
|
rot.RotateZ(DEG2RAD(-m_rotZ));
|
|
|
|
matrix3x3f shiftRot = matrix3x3f::Rotate(DEG2RAD(m_rotZ), { 0, 0, 1 });
|
|
|
|
const float moveSpeed = Pi::GetMoveSpeedShiftModifier();
|
|
float move = moveSpeed * frameTime * m_zoomClamped;
|
|
vector3f shift(0.0f);
|
|
bool manual_move = false; // used so that when you click on a system and move there, passing systems are not selected
|
|
if (InputBindings.mapViewMoveLeft->IsActive()) {
|
|
shift.x -= move * InputBindings.mapViewMoveLeft->GetValue();
|
|
manual_move = true;
|
|
}
|
|
if (InputBindings.mapViewMoveForward->IsActive()) {
|
|
shift.y += move * InputBindings.mapViewMoveForward->GetValue();
|
|
manual_move = true;
|
|
}
|
|
if (InputBindings.mapViewMoveUp->IsActive()) {
|
|
shift.z += move * InputBindings.mapViewMoveUp->GetValue();
|
|
manual_move = true;
|
|
}
|
|
m_posMovingTo += shift * shiftRot;
|
|
|
|
if (InputBindings.mapViewZoom->IsActive()) m_zoomMovingTo -= move * InputBindings.mapViewZoom->GetValue();
|
|
m_zoomMovingTo = Clamp(m_zoomMovingTo, 0.1f, FAR_MAX);
|
|
|
|
if (InputBindings.mapViewYaw->IsActive()) m_rotZMovingTo += 0.5f * moveSpeed * InputBindings.mapViewYaw->GetValue();
|
|
if (InputBindings.mapViewPitch->IsActive()) m_rotXMovingTo += 0.5f * moveSpeed * InputBindings.mapViewPitch->GetValue();
|
|
|
|
// to capture mouse when button was pressed and release when released
|
|
if (Pi::input->MouseButtonState(SDL_BUTTON_MIDDLE) != m_rotateWithMouseButton) {
|
|
m_rotateWithMouseButton = !m_rotateWithMouseButton;
|
|
Pi::input->SetCapturingMouse(m_rotateWithMouseButton);
|
|
}
|
|
|
|
if (m_rotateWithMouseButton || m_rotateView) {
|
|
int motion[2];
|
|
Pi::input->GetMouseMotion(motion);
|
|
m_rotXMovingTo += 0.2f * float(motion[1]);
|
|
m_rotZMovingTo += 0.2f * float(motion[0]);
|
|
} else if (m_zoomView) {
|
|
Pi::input->SetCapturingMouse(true);
|
|
int motion[2];
|
|
Pi::input->GetMouseMotion(motion);
|
|
m_zoomMovingTo += ZOOM_SPEED * 0.002f * motion[1];
|
|
}
|
|
|
|
m_rotXMovingTo = Clamp(m_rotXMovingTo, -170.0f, -10.0f);
|
|
|
|
{
|
|
vector3f diffPos = m_posMovingTo - m_pos;
|
|
vector3f travelPos = diffPos * 10.0f * frameTime;
|
|
if (travelPos.Length() > diffPos.Length())
|
|
m_pos = m_posMovingTo;
|
|
else
|
|
m_pos = m_pos + travelPos;
|
|
|
|
float diffX = m_rotXMovingTo - m_rotX;
|
|
float travelX = diffX * 10.0f * frameTime;
|
|
if (fabs(travelX) > fabs(diffX))
|
|
m_rotX = m_rotXMovingTo;
|
|
else
|
|
m_rotX = m_rotX + travelX;
|
|
|
|
float diffZ = m_rotZMovingTo - m_rotZ;
|
|
float travelZ = diffZ * 10.0f * frameTime;
|
|
if (fabs(travelZ) > fabs(diffZ))
|
|
m_rotZ = m_rotZMovingTo;
|
|
else
|
|
m_rotZ = m_rotZ + travelZ;
|
|
|
|
float diffZoom = m_zoomMovingTo - m_zoom;
|
|
float travelZoom = diffZoom * ZOOM_SPEED * frameTime;
|
|
if (fabs(travelZoom) > fabs(diffZoom))
|
|
m_zoom = m_zoomMovingTo;
|
|
else
|
|
m_zoom = m_zoom + travelZoom;
|
|
m_zoomClamped = Clamp(m_zoom, 1.f, FAR_LIMIT);
|
|
}
|
|
|
|
if (m_automaticSystemSelection && manual_move) {
|
|
SystemPath new_selected = SystemPath(int(floor(m_pos.x)), int(floor(m_pos.y)), int(floor(m_pos.z)), 0);
|
|
|
|
RefCountedPtr<Sector> ps = GetCached(new_selected);
|
|
if (ps->m_systems.size()) {
|
|
float px = FFRAC(m_pos.x) * Sector::SIZE;
|
|
float py = FFRAC(m_pos.y) * Sector::SIZE;
|
|
float pz = FFRAC(m_pos.z) * Sector::SIZE;
|
|
|
|
float min_dist = FLT_MAX;
|
|
for (unsigned int i = 0; i < ps->m_systems.size(); i++) {
|
|
Sector::System *ss = &ps->m_systems[i];
|
|
float dx = px - ss->GetPosition().x;
|
|
float dy = py - ss->GetPosition().y;
|
|
float dz = pz - ss->GetPosition().z;
|
|
float dist = sqrtf(dx * dx + dy * dy + dz * dz);
|
|
if (dist < min_dist) {
|
|
min_dist = dist;
|
|
new_selected.systemIndex = i;
|
|
}
|
|
}
|
|
|
|
if (!m_selected.IsSameSystem(new_selected)) {
|
|
RefCountedPtr<StarSystem> system = m_galaxy->GetStarSystem(new_selected);
|
|
SetSelected(CheckPathInRoute(system->GetStars()[0]->GetPath()));
|
|
}
|
|
}
|
|
}
|
|
|
|
ShrinkCache();
|
|
|
|
m_playerHyperspaceRange = LuaObject<Player>::CallMethod<float>(Pi::player, "GetHyperspaceRange");
|
|
|
|
if (!m_jumpSphere) {
|
|
Graphics::RenderStateDesc rsd;
|
|
rsd.blendMode = Graphics::BLEND_ALPHA;
|
|
rsd.depthTest = false;
|
|
rsd.depthWrite = false;
|
|
rsd.cullMode = Graphics::CULL_NONE;
|
|
m_jumpSphereState = m_renderer->CreateRenderState(rsd);
|
|
|
|
Graphics::MaterialDescriptor matdesc;
|
|
matdesc.effect = EFFECT_FRESNEL_SPHERE;
|
|
m_fresnelMat.Reset(m_renderer->CreateMaterial(matdesc));
|
|
m_fresnelMat->diffuse = Color::WHITE;
|
|
m_jumpSphere.reset(new Graphics::Drawables::Sphere3D(m_renderer, m_fresnelMat, m_jumpSphereState, 4, 1.0f));
|
|
}
|
|
}
|
|
|
|
void SectorView::ShowAll()
|
|
{
|
|
View::ShowAll();
|
|
}
|
|
|
|
void SectorView::MouseWheel(bool up)
|
|
{
|
|
if (this == Pi::GetView()) {
|
|
if (!up)
|
|
m_zoomMovingTo += ZOOM_SPEED * WHEEL_SENSITIVITY * Pi::GetMoveSpeedShiftModifier();
|
|
else
|
|
m_zoomMovingTo -= ZOOM_SPEED * WHEEL_SENSITIVITY * Pi::GetMoveSpeedShiftModifier();
|
|
}
|
|
}
|
|
|
|
void SectorView::ShrinkCache()
|
|
{
|
|
PROFILE_SCOPED()
|
|
// we're going to use these to determine if our sectors are within the range that we'll ever render
|
|
const int drawRadius = (m_zoomClamped <= FAR_THRESHOLD) ? DRAW_RAD : ceilf((m_zoomClamped / FAR_THRESHOLD) * DRAW_RAD);
|
|
|
|
const int xmin = int(floorf(m_pos.x)) - drawRadius;
|
|
const int xmax = int(floorf(m_pos.x)) + drawRadius;
|
|
const int ymin = int(floorf(m_pos.y)) - drawRadius;
|
|
const int ymax = int(floorf(m_pos.y)) + drawRadius;
|
|
const int zmin = int(floorf(m_pos.z)) - drawRadius;
|
|
const int zmax = int(floorf(m_pos.z)) + drawRadius;
|
|
|
|
// XXX don't clear the current/selected/target sectors
|
|
|
|
if (xmin != m_cacheXMin || xmax != m_cacheXMax || ymin != m_cacheYMin || ymax != m_cacheYMax || zmin != m_cacheZMin || zmax != m_cacheZMax) {
|
|
auto iter = m_sectorCache->Begin();
|
|
while (iter != m_sectorCache->End()) {
|
|
RefCountedPtr<Sector> s = iter->second;
|
|
//check_point_in_box
|
|
if (!s->WithinBox(xmin, xmax, ymin, ymax, zmin, zmax)) {
|
|
m_sectorCache->Erase(iter++);
|
|
} else {
|
|
iter++;
|
|
}
|
|
}
|
|
|
|
m_cacheXMin = xmin;
|
|
m_cacheXMax = xmax;
|
|
m_cacheYMin = ymin;
|
|
m_cacheYMax = ymax;
|
|
m_cacheZMin = zmin;
|
|
m_cacheZMax = zmax;
|
|
}
|
|
}
|
|
|
|
double SectorView::GetZoomLevel() const
|
|
{
|
|
return ((m_zoomClamped / FAR_THRESHOLD) * (OUTER_RADIUS)) + 0.5 * Sector::SIZE;
|
|
}
|
|
|
|
void SectorView::ZoomIn()
|
|
{
|
|
const float frameTime = Pi::GetFrameTime();
|
|
const float moveSpeed = Pi::GetMoveSpeedShiftModifier();
|
|
float move = moveSpeed * frameTime;
|
|
m_zoomMovingTo -= move;
|
|
m_zoomMovingTo = Clamp(m_zoomMovingTo, 0.1f, FAR_MAX);
|
|
}
|
|
|
|
void SectorView::ZoomOut()
|
|
{
|
|
const float frameTime = Pi::GetFrameTime();
|
|
const float moveSpeed = Pi::GetMoveSpeedShiftModifier();
|
|
float move = moveSpeed * frameTime;
|
|
m_zoomMovingTo += move;
|
|
m_zoomMovingTo = Clamp(m_zoomMovingTo, 0.1f, FAR_MAX);
|
|
}
|
|
|
|
vector3f SectorView::GetCenterSector()
|
|
{
|
|
return m_pos;
|
|
}
|
|
|
|
double SectorView::GetCenterDistance()
|
|
{
|
|
if (m_inSystem) {
|
|
vector3f dv = vector3f(floorf(m_pos.x) - m_current.sectorX, floorf(m_pos.y) - m_current.sectorY, floorf(m_pos.z) - m_current.sectorZ) * Sector::SIZE;
|
|
return dv.Length();
|
|
} else {
|
|
return 0.0;
|
|
}
|
|
}
|
|
|
|
std::vector<SystemPath> SectorView::GetNearbyStarSystemsByName(std::string pattern)
|
|
{
|
|
std::vector<SystemPath> result;
|
|
for (auto i = m_sectorCache->Begin(); i != m_sectorCache->End(); ++i) {
|
|
for (unsigned int systemIndex = 0; systemIndex < (*i).second->m_systems.size(); systemIndex++) {
|
|
const Sector::System *ss = &((*i).second->m_systems[systemIndex]);
|
|
|
|
// compare with the start of the current system
|
|
if (strncasecmp(pattern.c_str(), ss->GetName().c_str(), pattern.size()) == 0
|
|
// look for the pattern term somewhere within the current system
|
|
|| pi_strcasestr(ss->GetName().c_str(), pattern.c_str())) {
|
|
SystemPath match((*i).first);
|
|
match.systemIndex = systemIndex;
|
|
result.push_back(match);
|
|
}
|
|
// now also check other names of this system, if there are any
|
|
for (const std::string &other_name : ss->GetOtherNames()) {
|
|
if (strncasecmp(pattern.c_str(), other_name.c_str(), pattern.size()) == 0
|
|
// look for the pattern term somewhere within the current system
|
|
|| pi_strcasestr(other_name.c_str(), pattern.c_str())) {
|
|
SystemPath match((*i).first);
|
|
match.systemIndex = systemIndex;
|
|
result.push_back(match);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void SectorView::SetFactionVisible(const Faction *faction, bool visible)
|
|
{
|
|
if (visible)
|
|
m_hiddenFactions.erase(faction);
|
|
else
|
|
m_hiddenFactions.insert(faction);
|
|
m_toggledFaction = true;
|
|
}
|
|
|
|
void SectorView::SetZoomMode(bool enable)
|
|
{
|
|
if (enable != m_zoomView) {
|
|
Pi::input->SetCapturingMouse(enable);
|
|
m_zoomView = enable;
|
|
if (m_zoomView) m_rotateView = false;
|
|
}
|
|
}
|
|
|
|
void SectorView::SetRotateMode(bool enable)
|
|
{
|
|
if (enable != m_rotateView) {
|
|
Pi::input->SetCapturingMouse(enable);
|
|
m_rotateView = enable;
|
|
if (m_rotateView) m_zoomView = false;
|
|
}
|
|
}
|
|
|
|
void SectorView::ResetView()
|
|
{
|
|
GotoSystem(m_current);
|
|
m_rotXMovingTo = m_rotXDefault;
|
|
m_rotZMovingTo = m_rotZDefault;
|
|
m_zoomMovingTo = m_zoomDefault;
|
|
}
|