pioneer/src/SystemView.cpp

413 lines
13 KiB
C++
Raw Normal View History

2012-12-31 17:36:52 -08:00
// Copyright © 2008-2013 Pioneer Developers. See AUTHORS.txt for details
2012-09-15 17:59:15 -07:00
// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
2012-09-12 04:38:30 -07:00
#include "SystemView.h"
#include "Pi.h"
#include "SectorView.h"
#include "galaxy/StarSystem.h"
#include "Lang.h"
#include "StringF.h"
#include "Space.h"
#include "Player.h"
#include "FloatComparison.h"
#include "Game.h"
2012-09-09 13:30:11 -07:00
#include "AnimationCurves.h"
#include "graphics/Material.h"
#include "graphics/Renderer.h"
using namespace Graphics;
const double SystemView::PICK_OBJECT_RECT_SIZE = 12.0;
static const float MIN_ZOOM = 1e-30f; // Just to avoid having 0
static const float MAX_ZOOM = 1e30f;
static const float ZOOM_IN_SPEED = 2;
static const float ZOOM_OUT_SPEED = 1.f/ZOOM_IN_SPEED;
static const float WHEEL_SENSITIVITY = .2f; // Should be a variable in user settings.
SystemView::SystemView()
{
SetTransparency(true);
m_realtime = true;
2011-07-21 00:07:09 -07:00
Gui::Screen::PushFont("OverlayFont");
m_objectLabels = new Gui::LabelSet();
Add(m_objectLabels, 0, 0);
2011-07-21 00:07:09 -07:00
Gui::Screen::PopFont();
m_timePoint = (new Gui::Label(""))->Color(0.7f, 0.7f, 0.7f);
Add(m_timePoint, 2, Gui::Screen::GetHeight()-Gui::Screen::GetFontHeight()-66);
m_infoLabel = (new Gui::Label(""))->Color(0.7f, 0.7f, 0.7f);
Add(m_infoLabel, 2, 0);
m_infoText = (new Gui::Label(""))->Color(0.7f, 0.7f, 0.7f);
Add(m_infoText, 200, 0);
m_zoomInButton = new Gui::ImageButton("icons/zoom_in.png");
m_zoomInButton->SetToolTip(Lang::ZOOM_IN);
m_zoomInButton->SetRenderDimensions(30, 22);
Add(m_zoomInButton, 700, 5);
m_zoomOutButton = new Gui::ImageButton("icons/zoom_out.png");
m_zoomOutButton->SetToolTip(Lang::ZOOM_OUT);
m_zoomOutButton->SetRenderDimensions(30, 22);
Add(m_zoomOutButton, 732, 5);
2013-02-22 21:08:29 -08:00
const int time_controls_left = Gui::Screen::GetWidth() - 150;
const int time_controls_top = Gui::Screen::GetHeight() - 86;
Gui::ImageButton *b = new Gui::ImageButton("icons/sysview_accel_r3.png", "icons/sysview_accel_r3_on.png");
b->onPress.connect(sigc::bind(sigc::mem_fun(this, &SystemView::OnClickAccel), -10000000.f));
b->onRelease.connect(sigc::bind(sigc::mem_fun(this, &SystemView::OnClickAccel), 0.0f));
b->SetRenderDimensions(26, 17);
Add(b, time_controls_left + 0, time_controls_top);
b = new Gui::ImageButton("icons/sysview_accel_r2.png", "icons/sysview_accel_r2_on.png");
b->onPress.connect(sigc::bind(sigc::mem_fun(this, &SystemView::OnClickAccel), -1000000.f));
b->onRelease.connect(sigc::bind(sigc::mem_fun(this, &SystemView::OnClickAccel), 0.0f));
b->SetRenderDimensions(19, 17);
Add(b, time_controls_left + 26, time_controls_top);
b = new Gui::ImageButton("icons/sysview_accel_r1.png", "icons/sysview_accel_r1_on.png");
b->onPress.connect(sigc::bind(sigc::mem_fun(this, &SystemView::OnClickAccel), -100000.f));
b->onRelease.connect(sigc::bind(sigc::mem_fun(this, &SystemView::OnClickAccel), 0.0f));
b->SetRenderDimensions(19, 17);
Add(b, time_controls_left + 45, time_controls_top);
2013-02-22 21:08:29 -08:00
b = new Gui::ImageButton("icons/sysview_accel_rl.png", "icons/sysview_accel_rl_on.png");
b->onPress.connect(sigc::mem_fun(this, &SystemView::OnClickRealt));
2013-02-22 21:08:29 -08:00
b->SetRenderDimensions(19, 17);
Add(b, time_controls_left + 64, time_controls_top);
b = new Gui::ImageButton("icons/sysview_accel_f1.png", "icons/sysview_accel_f1_on.png");
b->onPress.connect(sigc::bind(sigc::mem_fun(this, &SystemView::OnClickAccel), 100000.f));
b->onRelease.connect(sigc::bind(sigc::mem_fun(this, &SystemView::OnClickAccel), 0.0f));
b->SetRenderDimensions(19, 17);
2013-02-22 21:08:29 -08:00
Add(b, time_controls_left + 83, time_controls_top);
b = new Gui::ImageButton("icons/sysview_accel_f2.png", "icons/sysview_accel_f2_on.png");
b->onPress.connect(sigc::bind(sigc::mem_fun(this, &SystemView::OnClickAccel), 1000000.f));
b->onRelease.connect(sigc::bind(sigc::mem_fun(this, &SystemView::OnClickAccel), 0.0f));
b->SetRenderDimensions(19, 17);
2013-02-22 21:08:29 -08:00
Add(b, time_controls_left + 102, time_controls_top);
b = new Gui::ImageButton("icons/sysview_accel_f3.png", "icons/sysview_accel_f3_on.png");
b->onPress.connect(sigc::bind(sigc::mem_fun(this, &SystemView::OnClickAccel), 10000000.f));
b->onRelease.connect(sigc::bind(sigc::mem_fun(this, &SystemView::OnClickAccel), 0.0f));
b->SetRenderDimensions(26, 17);
2013-02-22 21:08:29 -08:00
Add(b, time_controls_left + 121, time_controls_top);
m_onMouseButtonDown =
Pi::onMouseButtonDown.connect(sigc::mem_fun(this, &SystemView::MouseButtonDown));
ResetViewpoint();
}
SystemView::~SystemView()
{
m_onMouseButtonDown.disconnect();
}
void SystemView::OnClickAccel(float step)
{
m_realtime = false;
m_timeStep = step;
}
void SystemView::OnClickRealt()
{
m_realtime = true;
}
void SystemView::ResetViewpoint()
{
m_selectedObject = 0;
m_rot_z = 0;
m_rot_x = 50;
2012-04-02 17:13:51 -07:00
m_zoom = 1.0f/float(AU);
m_zoomTo = m_zoom;
m_timeStep = 1.0f;
2011-11-21 13:33:03 -08:00
m_time = Pi::game->GetTime();
}
void SystemView::PutOrbit(Orbit *orb, vector3d offset, Color color, double planetRadius)
{
vector3f vts[100];
bool hideAllFollowing = false; // hide all further points because we crushed to planet
for (int i = 0; i < int(COUNTOF(vts)); ++i) {
const double t = double(i) / double(COUNTOF(vts));
vector3d pos = orb->EvenSpacedPosTrajectory(t);
if(pos.Length() < planetRadius)
hideAllFollowing = true;
if(hideAllFollowing && i > 0)
vts[i] = vts[i-1];
else
vts[i] = vector3f(offset + pos * double(m_zoom));
}
if(orb->eccentricity < 1 && !hideAllFollowing) // not close the loop for hyperbolas and parabolas and crashed ellipses
m_renderer->DrawLines(COUNTOF(vts), vts, color, LINE_LOOP);
else
m_renderer->DrawLines(COUNTOF(vts), vts, color, LINE_STRIP);
}
2012-04-17 18:53:53 -07:00
void SystemView::OnClickObject(SystemBody *b)
{
m_selectedObject = b;
std::string desc;
std::string data;
desc += std::string(Lang::NAME);
desc += ":\n";
data += b->name+"\n";
desc += std::string(Lang::DAY_LENGTH);
desc += std::string(Lang::ROTATIONAL_PERIOD);
desc += ":\n";
data += stringf(Lang::N_DAYS, formatarg("days", b->rotationPeriod.ToFloat())) + "\n";
desc += std::string(Lang::RADIUS);
desc += ":\n";
data += format_distance(b->GetRadius())+"\n";
if (b->parent) {
desc += std::string(Lang::SEMI_MAJOR_AXIS);
desc += ":\n";
data += format_distance(b->orbit.semiMajorAxis)+"\n";
desc += std::string(Lang::ORBITAL_PERIOD);
desc += ":\n";
data += stringf(Lang::N_DAYS, formatarg("days", b->orbit.Period() / (24*60*60))) + "\n";
}
m_infoLabel->SetText(desc);
m_infoText->SetText(data);
if (Pi::KeyState(SDLK_LSHIFT) || Pi::KeyState(SDLK_RSHIFT)) {
SystemPath path = m_system->GetPathOf(b);
if (Pi::game->GetSpace()->GetStarSystem()->GetPath() == m_system->GetPath()) {
Body* body = Pi::game->GetSpace()->FindBodyForPath(&path);
if (body != 0)
Pi::player->SetNavTarget(body);
}
}
}
2012-04-17 18:53:53 -07:00
void SystemView::PutLabel(SystemBody *b, vector3d offset)
{
Gui::Screen::EnterOrtho();
vector3d pos;
if (Gui::Screen::Project(offset, pos)) {
// libsigc++ is a beautiful thing
m_objectLabels->Add(b->name, sigc::bind(sigc::mem_fun(this, &SystemView::OnClickObject), b), pos.x, pos.y);
}
Gui::Screen::LeaveOrtho();
glDisable(GL_LIGHTING);
}
// i don't know how to name it
#define ROUGH_SIZE_OF_TURD 10.0
void SystemView::PutBody(SystemBody *b, vector3d offset, const matrix4x4f &trans)
{
2012-04-17 18:53:53 -07:00
if (b->type == SystemBody::TYPE_STARPORT_SURFACE) return;
if (b->type != SystemBody::TYPE_GRAVPOINT) {
if (!m_bodyIcon.Valid()) {
m_bodyIcon.Reset(new Graphics::Drawables::Disk(m_renderer, Color::WHITE, 1.0f));
}
const double radius = b->GetRadius() * m_zoom;
matrix4x4f invRot = trans;
invRot.ClearToRotOnly();
invRot = invRot.InverseOf();
matrix4x4f bodyTrans = trans;
bodyTrans.Translate(vector3f(offset));
bodyTrans.Scale(radius);
m_renderer->SetTransform(bodyTrans * invRot);
m_bodyIcon->Draw(m_renderer);
m_renderer->SetTransform(trans);
PutLabel(b, offset);
}
Frame * fram = Pi::player->GetFrame();
if(fram->IsRotFrame()) fram = fram->GetNonRotFrame();
if(fram->GetSystemBody() == b && fram->GetSystemBody()->GetMass() > 0) {
Orbit * playerOrbit = Pi::player->ReturnOrbit();
PutOrbit(playerOrbit, offset, Color(1.0f, 0.0f, 0.0f), b->GetRadius());
PutSelectionBox(offset + playerOrbit->OrbitalPosAtTime(m_time)* double(m_zoom), Color(1.0f, 0.0f, 0.0f));
}
2012-04-17 18:53:53 -07:00
if (b->children.size()) for(std::vector<SystemBody*>::iterator kid = b->children.begin(); kid != b->children.end(); ++kid) {
if (is_zero_general((*kid)->orbit.semiMajorAxis)) continue;
if ((*kid)->orbit.semiMajorAxis * m_zoom < ROUGH_SIZE_OF_TURD) {
PutOrbit(&((*kid)->orbit), offset);
}
// not using current time yet
vector3d pos = (*kid)->orbit.OrbitalPosAtTime(m_time);
2011-05-10 17:57:37 -07:00
pos *= double(m_zoom);
//glTranslatef(pos.x, pos.y, pos.z);
PutBody(*kid, offset + pos, trans);
}
}
2012-04-17 18:53:53 -07:00
void SystemView::PutSelectionBox(const SystemBody *b, const vector3d &rootPos, const Color &col)
{
// surface starports just show the planet as being selected,
// because SystemView doesn't render terrains anyway
2012-04-17 18:53:53 -07:00
if (b->type == SystemBody::TYPE_STARPORT_SURFACE)
b = b->parent;
assert(b);
vector3d pos = rootPos;
2012-04-17 18:53:53 -07:00
// while (b->parent), not while (b) because the root SystemBody is defined to be at (0,0,0)
while (b->parent) {
pos += b->orbit.OrbitalPosAtTime(m_time) * double(m_zoom);
b = b->parent;
}
PutSelectionBox(pos, col);
}
void SystemView::PutSelectionBox(const vector3d &worldPos, const Color &col)
{
2012-01-19 19:14:16 -08:00
// XXX EnterOrtho shouldn't be necessary after Gui uses DrawLines2D correctly
Gui::Screen::EnterOrtho();
vector3d screenPos;
if (Gui::Screen::Project(worldPos, screenPos)) {
// XXX copied from WorldView::DrawTargetSquare -- these should be unified
const float x1 = float(screenPos.x - SystemView::PICK_OBJECT_RECT_SIZE * 0.5);
const float x2 = float(x1 + SystemView::PICK_OBJECT_RECT_SIZE);
const float y1 = float(screenPos.y - SystemView::PICK_OBJECT_RECT_SIZE * 0.5);
const float y2 = float(y1 + SystemView::PICK_OBJECT_RECT_SIZE);
2012-02-13 07:37:51 -08:00
const GLfloat vtx[8] = {
x1, y1,
x2, y1,
x2, y2,
x1, y2
};
glColor4f(col.r, col.g, col.b, col.a);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, vtx);
glDrawArrays(GL_LINE_LOOP, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
}
Gui::Screen::LeaveOrtho();
}
static const GLfloat fogDensity = 0.1f;
static const GLfloat fogColor[4] = { 0,0,0,1.0f };
2012-04-17 18:53:53 -07:00
void SystemView::GetTransformTo(SystemBody *b, vector3d &pos)
{
if (b->parent) {
GetTransformTo(b->parent, pos);
2011-05-10 17:57:37 -07:00
pos -= double(m_zoom) * b->orbit.OrbitalPosAtTime(m_time);
}
}
void SystemView::Draw3D()
{
m_renderer->SetPerspectiveProjection(50.f, Pi::GetScrAspect(), 1.f, 1000.f);
2012-02-13 07:37:51 -08:00
m_renderer->ClearScreen();
SystemPath path = Pi::sectorView->GetSelectedSystem();
if (m_system) {
if (!m_system->GetPath().IsSameSystem(path)) {
m_system.Reset();
ResetViewpoint();
}
}
if (m_realtime) {
m_time = Pi::game->GetTime();
}
else {
m_time += m_timeStep*Pi::GetFrameTime();
}
std::string t = Lang::TIME_POINT+format_date(m_time);
m_timePoint->SetText(t);
if (!m_system) m_system = StarSystem::GetCached(path);
2012-02-14 07:25:40 -08:00
// XXX fog is not going to be supported in renderer likely -
// fade the circles some other way
glEnable(GL_FOG);
glFogi(GL_FOG_MODE, GL_EXP2);
glFogfv(GL_FOG_COLOR, fogColor);
glFogf(GL_FOG_DENSITY, fogDensity);
glHint(GL_FOG_HINT, GL_NICEST);
2012-02-14 07:25:40 -08:00
matrix4x4f trans = matrix4x4f::Identity();
trans.Translate(0,0,-ROUGH_SIZE_OF_TURD);
trans.Rotate(DEG2RAD(m_rot_x), 1, 0, 0);
trans.Rotate(DEG2RAD(m_rot_z), 0, 0, 1);
m_renderer->SetTransform(trans);
vector3d pos(0,0,0);
if (m_selectedObject) GetTransformTo(m_selectedObject, pos);
m_objectLabels->Clear();
if (m_system->GetUnexplored())
m_infoLabel->SetText(Lang::UNEXPLORED_SYSTEM_NO_SYSTEM_VIEW);
else if (m_system->rootBody) {
PutBody(m_system->rootBody.Get(), pos, trans);
if (Pi::game->GetSpace()->GetStarSystem() == m_system) {
const Body *navTarget = Pi::player->GetNavTarget();
2012-04-17 18:53:53 -07:00
const SystemBody *navTargetSystemBody = navTarget ? navTarget->GetSystemBody() : 0;
if (navTargetSystemBody)
PutSelectionBox(navTargetSystemBody, pos, Color(0.0, 1.0, 0.0, 1.0));
}
}
glDisable(GL_FOG);
}
void SystemView::Update()
{
const float ft = Pi::GetFrameTime();
// XXX ugly hack checking for console here
if (!Pi::IsConsoleActive()) {
if (Pi::KeyState(SDLK_EQUALS) ||
m_zoomInButton->IsPressed())
m_zoomTo *= pow(ZOOM_IN_SPEED * Pi::GetMoveSpeedShiftModifier(), ft);
if (Pi::KeyState(SDLK_MINUS) ||
m_zoomOutButton->IsPressed())
m_zoomTo *= pow(ZOOM_OUT_SPEED / Pi::GetMoveSpeedShiftModifier(), ft);
}
// TODO: add "true" lower/upper bounds to m_zoomTo / m_zoom
m_zoomTo = Clamp(m_zoomTo, MIN_ZOOM, MAX_ZOOM);
m_zoom = Clamp(m_zoom, MIN_ZOOM, MAX_ZOOM);
2012-09-09 13:30:11 -07:00
AnimationCurves::Approach(m_zoom, m_zoomTo, ft);
if (Pi::MouseButtonState(SDL_BUTTON_RIGHT)) {
int motion[2];
Pi::GetMouseMotion(motion);
m_rot_x += motion[1]*20*ft;
m_rot_z += motion[0]*20*ft;
}
}
void SystemView::MouseButtonDown(int button, int x, int y)
{
2012-01-19 18:43:29 -08:00
if (this == Pi::GetView()) {
if (Pi::MouseButtonState(SDL_BUTTON_WHEELDOWN))
m_zoomTo *= ((ZOOM_OUT_SPEED-1) * WHEEL_SENSITIVITY+1) / Pi::GetMoveSpeedShiftModifier();
else if (Pi::MouseButtonState(SDL_BUTTON_WHEELUP))
m_zoomTo *= ((ZOOM_IN_SPEED-1) * WHEEL_SENSITIVITY+1) * Pi::GetMoveSpeedShiftModifier();
2012-01-19 18:43:29 -08:00
}
}