1085 lines
26 KiB
C++
1085 lines
26 KiB
C++
// main.cpp
|
|
//
|
|
// Copyright (C) 2000, Chris Laurel <claurel@shatters.net>
|
|
//
|
|
// Windows front end for Celestia.
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <cstdlib>
|
|
#include <cctype>
|
|
#include <time.h>
|
|
#include <windows.h>
|
|
#include <commctrl.h>
|
|
#include "gl.h"
|
|
#include "celestia.h"
|
|
#include "vecmath.h"
|
|
#include "quaternion.h"
|
|
#include "stardb.h"
|
|
#include "solarsys.h"
|
|
#include "visstars.h"
|
|
#include "mathlib.h"
|
|
#include "astro.h"
|
|
#include "config.h"
|
|
#include "simulation.h"
|
|
|
|
#include "../res/resource.h"
|
|
|
|
|
|
//----------------------------------
|
|
// Skeleton functions and variables.
|
|
//-----------------
|
|
char szAppName[] = "Celestia";
|
|
float fTime=0.f, fDeltaTime=0.f;
|
|
|
|
|
|
//----------------------------------
|
|
// Timer info.
|
|
LARGE_INTEGER TimerFreq; // Timer Frequency.
|
|
LARGE_INTEGER TimeStart; // Time of start.
|
|
LARGE_INTEGER TimeCur; // Current time.
|
|
|
|
|
|
static bool fullscreen;
|
|
static int lastX = 0, lastY = 0;
|
|
static int mouseMotion = 0;
|
|
float xrot = 0, yrot = 0;
|
|
static bool upPress = false;
|
|
static bool downPress = false;
|
|
static bool leftPress = false;
|
|
static bool rightPress = false;
|
|
static bool pgupPress = false;
|
|
static bool pgdnPress = false;
|
|
static bool wireframe = false;
|
|
|
|
static bool paused = false;
|
|
static double timeScale = 0.0;
|
|
|
|
static bool textEnterMode = false;
|
|
|
|
static Point3f initialPosition(0, 0, 0);
|
|
static Point3f position;
|
|
static Vec3f velocity;
|
|
static float distanceFromCenter = 0;
|
|
|
|
static CelestiaConfig* config = NULL;
|
|
|
|
static StarDatabase* starDB = NULL;
|
|
static StarNameDatabase* starNameDB = NULL;
|
|
static SolarSystemCatalog* solarSystemCatalog = NULL;
|
|
|
|
static Simulation* sim = NULL;
|
|
static Renderer* renderer = NULL;
|
|
|
|
HINSTANCE appInstance;
|
|
|
|
static HMENU menuBar = 0;
|
|
static HACCEL acceleratorTable = 0;
|
|
|
|
bool cursorVisible = true;
|
|
|
|
#define INFINITE_MOUSE
|
|
|
|
#define ROTATION_SPEED 6
|
|
#define ACCELERATION 20.0f
|
|
|
|
|
|
#define MENU_CHOOSE_PLANET 32000
|
|
|
|
|
|
// Good 'ol generic drawing stuff.
|
|
LRESULT CALLBACK SkeletonProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
|
|
|
|
|
|
|
|
bool ReadStars(string starsFileName, string namesFileName)
|
|
{
|
|
ifstream starFile(starsFileName.c_str(), ios::in | ios::binary);
|
|
if (!starFile.good())
|
|
{
|
|
cerr << "Error opening " << starsFileName << '\n';
|
|
return false;
|
|
}
|
|
|
|
ifstream starNamesFile(namesFileName.c_str(), ios::in);
|
|
if (!starNamesFile.good())
|
|
{
|
|
cerr << "Error opening " << namesFileName << '\n';
|
|
return false;
|
|
}
|
|
|
|
starDB = StarDatabase::read(starFile);
|
|
if (starDB == NULL)
|
|
{
|
|
cerr << "Error reading stars file\n";
|
|
return false;
|
|
}
|
|
|
|
starNameDB = StarDatabase::readNames(starNamesFile);
|
|
if (starNameDB == NULL)
|
|
{
|
|
cerr << "Error reading star names file\n";
|
|
return false;
|
|
}
|
|
|
|
starDB->setNameDatabase(starNameDB);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void ChangeDisplayMode()
|
|
{
|
|
DEVMODE device_mode;
|
|
|
|
memset(&device_mode, 0, sizeof(DEVMODE));
|
|
|
|
device_mode.dmSize = sizeof(DEVMODE);
|
|
|
|
device_mode.dmPelsWidth = 800;
|
|
device_mode.dmPelsHeight = 600;
|
|
device_mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
|
|
|
|
ChangeDisplaySettings(&device_mode, CDS_FULLSCREEN);
|
|
}
|
|
|
|
|
|
void RestoreDisplayMode()
|
|
{
|
|
ChangeDisplaySettings(0, 0);
|
|
}
|
|
|
|
|
|
bool LoadItemTextFromFile(HWND hWnd,
|
|
int item,
|
|
char* filename)
|
|
{
|
|
ifstream textFile(filename, ios::in | ios::binary);
|
|
string s;
|
|
|
|
char c;
|
|
while (textFile.get(c))
|
|
{
|
|
if (c == '\012' || c == '\014')
|
|
s += "\r\r\n";
|
|
else
|
|
s += c;
|
|
}
|
|
|
|
if (textFile.bad())
|
|
return false;
|
|
|
|
SetDlgItemText(hWnd, item, s.c_str());
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
BOOL APIENTRY AboutProc(HWND hDlg,
|
|
UINT message,
|
|
UINT wParam,
|
|
LONG lParam)
|
|
{
|
|
switch (message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
return(TRUE);
|
|
|
|
case WM_COMMAND:
|
|
if (LOWORD(wParam) == IDOK)
|
|
{
|
|
EndDialog(hDlg, 0);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL APIENTRY LicenseProc(HWND hDlg,
|
|
UINT message,
|
|
UINT wParam,
|
|
LONG lParam)
|
|
{
|
|
switch (message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
LoadItemTextFromFile(hDlg, IDC_LICENSE_TEXT, "License.txt");
|
|
return(TRUE);
|
|
|
|
case WM_COMMAND:
|
|
if (LOWORD(wParam) == IDOK)
|
|
{
|
|
EndDialog(hDlg, 0);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL APIENTRY FindObjectProc(HWND hDlg,
|
|
UINT message,
|
|
UINT wParam,
|
|
LONG lParam)
|
|
{
|
|
switch (message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
return(TRUE);
|
|
|
|
case WM_COMMAND:
|
|
if (LOWORD(wParam) == IDOK)
|
|
{
|
|
char buf[1024];
|
|
int len = GetDlgItemText(hDlg, IDC_FINDOBJECT_EDIT, buf, 1024);
|
|
if (len > 0)
|
|
sim->selectBody(string(buf));
|
|
EndDialog(hDlg, 0);
|
|
return TRUE;
|
|
}
|
|
else if (LOWORD(wParam) == IDCANCEL)
|
|
{
|
|
EndDialog(hDlg, 0);
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL APIENTRY SetTimeProc(HWND hDlg,
|
|
UINT message,
|
|
UINT wParam,
|
|
LONG lParam)
|
|
{
|
|
switch (message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
return(TRUE);
|
|
|
|
case WM_COMMAND:
|
|
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
|
|
{
|
|
EndDialog(hDlg, 0);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
HMENU CreateMenuBar()
|
|
{
|
|
return LoadMenu(appInstance, MAKEINTRESOURCE(IDR_MAIN_MENU));
|
|
}
|
|
|
|
|
|
|
|
VOID APIENTRY handlePopupMenu(HWND hwnd, POINT point,
|
|
const SolarSystem& solarSystem)
|
|
{
|
|
HMENU hMenu;
|
|
|
|
hMenu = CreatePopupMenu();
|
|
PlanetarySystem* planets = solarSystem.getPlanets();
|
|
for (int i = 0; i < planets->getSystemSize(); i++)
|
|
{
|
|
Body* body = planets->getBody(i);
|
|
AppendMenu(hMenu, MF_STRING, MENU_CHOOSE_PLANET + i,
|
|
body->getName().c_str());
|
|
}
|
|
|
|
ClientToScreen (hwnd, (LPPOINT) &point);
|
|
|
|
TrackPopupMenu (hMenu, 0, point.x, point.y, 0, hwnd, NULL);
|
|
|
|
DestroyMenu (hMenu);
|
|
}
|
|
|
|
|
|
void handleKey(WPARAM key, bool down)
|
|
{
|
|
switch (key) {
|
|
case VK_UP:
|
|
upPress = down;
|
|
break;
|
|
case VK_DOWN:
|
|
downPress = down;
|
|
break;
|
|
case VK_LEFT:
|
|
leftPress = down;
|
|
break;
|
|
case VK_RIGHT:
|
|
rightPress = down;
|
|
break;
|
|
case VK_HOME:
|
|
pgupPress = down;
|
|
break;
|
|
case VK_END:
|
|
pgdnPress = down;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void handleKeyPress(int c)
|
|
{
|
|
bool shift = ((GetKeyState(VK_SHIFT) & 0x8000) != 0);
|
|
if (textEnterMode)
|
|
{
|
|
if (c == ' ' || isalpha(c) || isdigit(c) || ispunct(c))
|
|
{
|
|
if (!shift && isalpha(c))
|
|
c = tolower(c);
|
|
sim->typeChar(c);
|
|
}
|
|
return;
|
|
}
|
|
|
|
switch (c)
|
|
{
|
|
case 'A':
|
|
if (sim->getTargetSpeed() == 0)
|
|
sim->setTargetSpeed(0.000001f);
|
|
else
|
|
sim->setTargetSpeed(sim->getTargetSpeed() * 10.0f);
|
|
break;
|
|
|
|
case VK_F1:
|
|
sim->setTargetSpeed(0);
|
|
break;
|
|
|
|
case VK_F2:
|
|
sim->setTargetSpeed(astro::kilometersToLightYears(1.0));
|
|
break;
|
|
|
|
case VK_F3:
|
|
sim->setTargetSpeed(astro::kilometersToLightYears(1000.0));
|
|
break;
|
|
|
|
case VK_F4:
|
|
sim->setTargetSpeed(astro::kilometersToLightYears(1000000.0));
|
|
break;
|
|
|
|
case VK_F5:
|
|
sim->setTargetSpeed(astro::AUtoLightYears(1));
|
|
break;
|
|
|
|
case VK_F6:
|
|
sim->setTargetSpeed(1);
|
|
break;
|
|
|
|
case 'Z':
|
|
sim->setTargetSpeed(sim->getTargetSpeed() * 0.1f);
|
|
break;
|
|
|
|
case 'S':
|
|
sim->setTargetSpeed(0);
|
|
break;
|
|
|
|
case 'Q':
|
|
sim->setTargetSpeed(-sim->getTargetSpeed());
|
|
break;
|
|
|
|
case 'X':
|
|
sim->setTargetSpeed(sim->getTargetSpeed());
|
|
break;
|
|
|
|
case 'G':
|
|
sim->gotoSelection();
|
|
break;
|
|
|
|
case 'C':
|
|
sim->centerSelection();
|
|
break;
|
|
|
|
case 'F':
|
|
sim->follow();
|
|
break;
|
|
|
|
case 'H':
|
|
sim->selectStar(0);
|
|
break;
|
|
|
|
case 'V':
|
|
sim->setHUDDetail((sim->getHUDDetail() + 1) % 2);
|
|
break;
|
|
|
|
case ',':
|
|
if (renderer->getFieldOfView() > 1.0f)
|
|
renderer->setFieldOfView(renderer->getFieldOfView() / 1.1f);
|
|
break;
|
|
|
|
case '.':
|
|
if (renderer->getFieldOfView() < 120.0f)
|
|
renderer->setFieldOfView(renderer->getFieldOfView() * 1.1f);
|
|
break;
|
|
|
|
case 'K':
|
|
sim->setTimeScale(0.1 * sim->getTimeScale());
|
|
break;
|
|
|
|
case 'L':
|
|
sim->setTimeScale(10.0 * sim->getTimeScale());
|
|
break;
|
|
|
|
case 'N':
|
|
renderer->setLabelMode(renderer->getLabelMode() ^ Renderer::PlanetLabels);
|
|
break;
|
|
|
|
case 'O':
|
|
renderer->setLabelMode(renderer->getLabelMode() ^ Renderer::PlanetOrbits);
|
|
break;
|
|
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
sim->selectPlanet(c - '1');
|
|
break;
|
|
|
|
case '0':
|
|
sim->selectPlanet(-1);
|
|
break;
|
|
|
|
case 'W':
|
|
wireframe = !wireframe;
|
|
renderer->setRenderMode(wireframe ? GL_LINE : GL_FILL);
|
|
break;
|
|
|
|
case ' ':
|
|
if (paused)
|
|
{
|
|
sim->setTimeScale(timeScale);
|
|
CheckMenuItem(menuBar, ID_TIME_FREEZE, MF_UNCHECKED);
|
|
}
|
|
else
|
|
{
|
|
timeScale = sim->getTimeScale();
|
|
sim->setTimeScale(0.0);
|
|
CheckMenuItem(menuBar, ID_TIME_FREEZE, MF_CHECKED);
|
|
}
|
|
paused = !paused;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// Select the pixel format for a given device context
|
|
void SetDCPixelFormat(HDC hDC)
|
|
{
|
|
int nPixelFormat;
|
|
|
|
static PIXELFORMATDESCRIPTOR pfd = {
|
|
sizeof(PIXELFORMATDESCRIPTOR), // Size of this structure
|
|
1, // Version of this structure
|
|
PFD_DRAW_TO_WINDOW | // Draw to Window (not to bitmap)
|
|
PFD_SUPPORT_OPENGL | // Support OpenGL calls in window
|
|
PFD_DOUBLEBUFFER, // Double buffered mode
|
|
PFD_TYPE_RGBA, // RGBA Color mode
|
|
GetDeviceCaps(hDC, BITSPIXEL), // Want the display bit depth
|
|
0,0,0,0,0,0, // Not used to select mode
|
|
0,0, // Not used to select mode
|
|
0,0,0,0,0, // Not used to select mode
|
|
16, // Size of depth buffer
|
|
0, // Not used to select mode
|
|
0, // Not used to select mode
|
|
PFD_MAIN_PLANE, // Draw in main plane
|
|
0, // Not used to select mode
|
|
0,0,0 // Not used to select mode
|
|
};
|
|
|
|
// Choose a pixel format that best matches that described in pfd
|
|
nPixelFormat = ChoosePixelFormat(hDC, &pfd);
|
|
|
|
// Set the pixel format for the device context
|
|
SetPixelFormat(hDC, nPixelFormat, &pfd);
|
|
}
|
|
|
|
|
|
void ChangeSize(GLsizei w, GLsizei h)
|
|
{
|
|
if (h == 0)
|
|
h = 1;
|
|
|
|
glViewport(0, 0, w, h);
|
|
if (renderer != NULL)
|
|
renderer->resize(w, h);
|
|
}
|
|
|
|
|
|
GLsizei g_w, g_h;
|
|
int bReady;
|
|
|
|
|
|
// Good ol' creation code.
|
|
int APIENTRY WinMain(HINSTANCE hInstance,
|
|
HINSTANCE hPrevInstance,
|
|
LPSTR lpCmdLine,
|
|
int nCmdShow)
|
|
{
|
|
// Say we're not ready to render yet.
|
|
bReady = 0;
|
|
|
|
appInstance = hInstance;
|
|
|
|
position = Point3f(0, 0, 0);
|
|
velocity = Vec3f(0, 0, 0);
|
|
|
|
// Setup the window class.
|
|
WNDCLASS wc;
|
|
HWND hWnd;
|
|
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
|
|
wc.lpfnWndProc = (WNDPROC)SkeletonProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = hInstance;
|
|
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_CELESTIA_ICON));
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
|
|
wc.hbrBackground = NULL;
|
|
wc.lpszMenuName = NULL;
|
|
wc.lpszClassName = szAppName;
|
|
|
|
if (strstr(lpCmdLine, "-fullscreen"))
|
|
fullscreen = true;
|
|
else
|
|
fullscreen = false;
|
|
|
|
if (RegisterClass(&wc) == 0)
|
|
{
|
|
MessageBox(NULL,
|
|
"Failed to register the window class.", "Fatal Error",
|
|
MB_OK | MB_ICONERROR);
|
|
return FALSE;
|
|
}
|
|
|
|
// Check for the presence of the license file--don't run unless it's there.
|
|
{
|
|
ifstream license("License.txt");
|
|
if (!license.good())
|
|
{
|
|
MessageBox(NULL,
|
|
"License file 'License.txt' is missing!", "Fatal Error",
|
|
MB_OK | MB_ICONERROR);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
config = ReadCelestiaConfig("celestia.cfg");
|
|
if (config == NULL)
|
|
{
|
|
MessageBox(NULL,
|
|
"Error reading configuration file.", "Fatal Error",
|
|
MB_OK | MB_ICONERROR);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!ReadStars(config->starDatabaseFile, config->starNamesFile))
|
|
{
|
|
MessageBox(NULL,
|
|
"Cannot read star database",
|
|
"Error",
|
|
MB_OK | MB_ICONERROR);
|
|
return FALSE;
|
|
}
|
|
|
|
solarSystemCatalog = new SolarSystemCatalog();
|
|
{
|
|
for (vector<string>::const_iterator iter = config->solarSystemFiles.begin();
|
|
iter != config->solarSystemFiles.end();
|
|
iter++)
|
|
{
|
|
ifstream solarSysFile(iter->c_str(), ios::in);
|
|
if (!solarSysFile.good())
|
|
{
|
|
cout << "Error opening " << iter->c_str() << '\n';
|
|
}
|
|
else
|
|
{
|
|
ReadSolarSystems(solarSysFile, *starDB, *solarSystemCatalog);
|
|
}
|
|
}
|
|
}
|
|
|
|
sim = new Simulation();
|
|
sim->setStarDatabase(starDB, solarSystemCatalog);
|
|
|
|
// Set the simulation starting time to the current system time
|
|
sim->setTime((double) time(NULL) + (double) astro::Date(1970, 1, 1) * 86400.0);
|
|
|
|
if (!fullscreen)
|
|
{
|
|
|
|
menuBar = CreateMenuBar();
|
|
acceleratorTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATORS));
|
|
|
|
hWnd = CreateWindow(szAppName,
|
|
szAppName,
|
|
WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|WS_CLIPSIBLINGS,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
800, 600,
|
|
NULL,
|
|
menuBar,
|
|
hInstance,
|
|
NULL );
|
|
}
|
|
else
|
|
{
|
|
hWnd = CreateWindow(szAppName,
|
|
szAppName,
|
|
WS_POPUP,
|
|
0, 0,
|
|
800, 600,
|
|
NULL, NULL,
|
|
hInstance,
|
|
NULL );
|
|
}
|
|
|
|
if (hWnd == NULL)
|
|
{
|
|
MessageBox(NULL,
|
|
"Failed to create the application window.",
|
|
"Fatal Error",
|
|
MB_OK | MB_ICONERROR);
|
|
return FALSE;
|
|
}
|
|
|
|
ShowWindow(hWnd, SW_SHOW);
|
|
UpdateWindow(hWnd);
|
|
|
|
// Initialize common controls
|
|
INITCOMMONCONTROLSEX icex;
|
|
icex.dwSize = sizeof(icex);
|
|
icex.dwICC = ICC_DATE_CLASSES;
|
|
InitCommonControlsEx(&icex);
|
|
|
|
// Reset the timer
|
|
QueryPerformanceFrequency(&TimerFreq);
|
|
QueryPerformanceCounter(&TimeStart);
|
|
|
|
renderer = new Renderer();
|
|
|
|
// Prepare the scene for rendering.
|
|
if (!renderer->init((int) g_w, (int) g_h)) {
|
|
MessageBox(hWnd,
|
|
"Failed to initialize",
|
|
"Fatal Blow",
|
|
MB_OK | MB_ICONERROR);
|
|
return FALSE;
|
|
}
|
|
|
|
// Set up the star labels
|
|
for (vector<string>::const_iterator iter = config->labelledStars.begin();
|
|
iter != config->labelledStars.end();
|
|
iter++)
|
|
{
|
|
Star* star = starDB->find(*iter);
|
|
if (star != NULL)
|
|
renderer->addLabelledStar(star);
|
|
}
|
|
|
|
// We're now ready.
|
|
bReady = 1;
|
|
|
|
// Usual running around in circles bit...
|
|
int bGotMsg;
|
|
MSG msg;
|
|
PeekMessage(&msg, NULL, 0U, 0U, PM_NOREMOVE);
|
|
while (msg.message != WM_QUIT)
|
|
{
|
|
// Use PeekMessage() if the app is active, so we can use idle time to
|
|
// render the scene. Else, use GetMessage() to avoid eating CPU time.
|
|
bGotMsg = PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE );
|
|
|
|
if (bGotMsg)
|
|
{
|
|
// Translate and dispatch the message
|
|
if (!TranslateAccelerator(hWnd, acceleratorTable, &msg))
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
else
|
|
{
|
|
InvalidateRect(hWnd, NULL, FALSE);
|
|
}
|
|
}
|
|
|
|
// Not ready to render anymore.
|
|
bReady = 0;
|
|
|
|
// Nuke all applicable scene stuff.
|
|
renderer->shutdown();
|
|
|
|
return msg.wParam;
|
|
}
|
|
|
|
|
|
static void ToggleLabelState(int menuItem, int labelState)
|
|
{
|
|
if ((GetMenuState(menuBar, menuItem, MF_BYCOMMAND) & MF_CHECKED) == 0)
|
|
{
|
|
renderer->setLabelMode(renderer->getLabelMode() | labelState);
|
|
CheckMenuItem(menuBar, menuItem, MF_CHECKED);
|
|
}
|
|
else
|
|
{
|
|
renderer->setLabelMode(renderer->getLabelMode() & ~labelState);
|
|
CheckMenuItem(menuBar, menuItem, MF_UNCHECKED);
|
|
}
|
|
}
|
|
|
|
|
|
static bool ToggleMenuItem(int menuItem)
|
|
{
|
|
if ((GetMenuState(menuBar, menuItem, MF_BYCOMMAND) & MF_CHECKED) == 0)
|
|
{
|
|
CheckMenuItem(menuBar, menuItem, MF_CHECKED);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
CheckMenuItem(menuBar, menuItem, MF_UNCHECKED);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
LRESULT CALLBACK SkeletonProc(HWND hWnd,
|
|
UINT uMsg,
|
|
WPARAM wParam, LPARAM lParam)
|
|
{
|
|
|
|
static HGLRC hRC;
|
|
static HDC hDC;
|
|
|
|
switch(uMsg) {
|
|
case WM_CREATE:
|
|
hDC = GetDC(hWnd);
|
|
if(fullscreen)
|
|
ChangeDisplayMode();
|
|
SetDCPixelFormat(hDC);
|
|
hRC = wglCreateContext(hDC);
|
|
wglMakeCurrent(hDC, hRC);
|
|
break;
|
|
|
|
case WM_MOUSEMOVE:
|
|
if ((wParam & (MK_LBUTTON | MK_RBUTTON)) != 0)
|
|
{
|
|
int x, y;
|
|
x = LOWORD(lParam);
|
|
y = HIWORD(lParam);
|
|
|
|
if ((wParam & (MK_LBUTTON | MK_RBUTTON)) == (MK_LBUTTON|MK_RBUTTON))
|
|
{
|
|
float amount = (float) (lastY - y) / g_h;
|
|
sim->changeOrbitDistance(amount * 5);
|
|
}
|
|
else
|
|
{
|
|
Quatf q(1);
|
|
// For a small field of view, rotate the camera more finely
|
|
float coarseness = renderer->getFieldOfView() / 30.0f;
|
|
q.yrotate((float) (x - lastX) / g_w * coarseness);
|
|
q.xrotate((float) (y - lastY) / g_h * coarseness);
|
|
if ((wParam & MK_RBUTTON) != 0)
|
|
sim->orbit(~q);
|
|
else
|
|
sim->setOrientation(sim->getOrientation() * q);
|
|
}
|
|
|
|
mouseMotion += abs(x - lastX) + abs(y - lastY);
|
|
|
|
#ifdef INFINITE_MOUSE
|
|
// A bit of mouse tweaking here . . . we want to allow the user to
|
|
// rotate and zoom continuously, without having to pick up the mouse
|
|
// every time it leaves the window. So, once we start dragging, we'll
|
|
// hide the mouse and reset it's position every time it's moved.
|
|
POINT pt;
|
|
pt.x = lastX;
|
|
pt.y = lastY;
|
|
ClientToScreen(hWnd, &pt);
|
|
if (x - lastX != 0 || y - lastY != 0)
|
|
SetCursorPos(pt.x, pt.y);
|
|
if (cursorVisible)
|
|
{
|
|
ShowCursor(FALSE);
|
|
cursorVisible = false;
|
|
}
|
|
#else
|
|
lastX = x;
|
|
lastY = y;
|
|
#endif // INFINITE_MOUSE
|
|
}
|
|
break;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
lastX = LOWORD(lParam);
|
|
lastY = HIWORD(lParam);
|
|
mouseMotion = 0;
|
|
break;
|
|
|
|
case WM_LBUTTONUP:
|
|
if (!cursorVisible)
|
|
{
|
|
ShowCursor(TRUE);
|
|
cursorVisible = true;
|
|
}
|
|
if (mouseMotion < 3)
|
|
{
|
|
Vec3f pickRay = renderer->getPickRay(LOWORD(lParam),
|
|
HIWORD(lParam));
|
|
Selection oldSel = sim->getSelection();
|
|
Selection newSel = sim->pickObject(pickRay);
|
|
if (!oldSel.empty() && oldSel == newSel)
|
|
sim->centerSelection();
|
|
}
|
|
break;
|
|
|
|
case WM_RBUTTONDOWN:
|
|
lastX = LOWORD(lParam);
|
|
lastY = HIWORD(lParam);
|
|
mouseMotion = 0;
|
|
break;
|
|
|
|
case WM_RBUTTONUP:
|
|
if (!cursorVisible)
|
|
{
|
|
ShowCursor(TRUE);
|
|
cursorVisible = true;
|
|
}
|
|
if (mouseMotion < 3)
|
|
{
|
|
POINT pt;
|
|
pt.x = LOWORD(lParam);
|
|
pt.y = HIWORD(lParam);
|
|
SolarSystem* solarsys = sim->getNearestSolarSystem();
|
|
if (solarsys != NULL)
|
|
handlePopupMenu(hWnd, pt, *solarsys);
|
|
}
|
|
break;
|
|
|
|
case WM_MOUSEWHEEL:
|
|
// The mouse wheel controls the zoom
|
|
{
|
|
short wheelMove = (short) HIWORD(wParam);
|
|
float factor = 1.0f;
|
|
if (wheelMove > 0)
|
|
{
|
|
if (renderer->getFieldOfView() > 0.1f)
|
|
renderer->setFieldOfView(renderer->getFieldOfView() / 1.1f);
|
|
}
|
|
else if (wheelMove < 0)
|
|
{
|
|
if (renderer->getFieldOfView() < 120.0f)
|
|
renderer->setFieldOfView(renderer->getFieldOfView() * 1.1f);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_MBUTTONDOWN:
|
|
renderer->setFieldOfView(45.0f);
|
|
break;
|
|
|
|
case WM_KEYDOWN:
|
|
switch (wParam)
|
|
{
|
|
case VK_ESCAPE:
|
|
DestroyWindow(hWnd);
|
|
break;
|
|
case VK_RETURN:
|
|
if (textEnterMode)
|
|
sim->typeChar('\n');
|
|
textEnterMode = !textEnterMode;
|
|
break;
|
|
case VK_UP:
|
|
case VK_DOWN:
|
|
case VK_LEFT:
|
|
case VK_RIGHT:
|
|
case VK_HOME:
|
|
case VK_END:
|
|
handleKey(wParam, true);
|
|
break;
|
|
default:
|
|
handleKeyPress(wParam);
|
|
}
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case ID_NAVIGATION_CENTER:
|
|
sim->centerSelection();
|
|
break;
|
|
case ID_NAVIGATION_GOTO:
|
|
sim->gotoSelection();
|
|
break;
|
|
case ID_NAVIGATION_FOLLOW:
|
|
sim->follow();
|
|
break;
|
|
case ID_NAVIGATION_HOME:
|
|
sim->selectStar(0);
|
|
break;
|
|
case ID_NAVIGATION_SELECT:
|
|
DialogBox(appInstance, MAKEINTRESOURCE(IDD_FINDOBJECT), hWnd, FindObjectProc);
|
|
break;
|
|
|
|
case ID_RENDER_SHOWHUDTEXT:
|
|
sim->setHUDDetail(ToggleMenuItem(ID_RENDER_SHOWHUDTEXT) ? 1 : 0);
|
|
break;
|
|
case ID_RENDER_SHOWPLANETLABELS:
|
|
ToggleLabelState(ID_RENDER_SHOWPLANETLABELS, Renderer::PlanetLabels);
|
|
break;
|
|
case ID_RENDER_SHOWSTARLABELS:
|
|
ToggleLabelState(ID_RENDER_SHOWSTARLABELS, Renderer::StarLabels);
|
|
break;
|
|
case ID_RENDER_SHOWORBITS:
|
|
ToggleLabelState(ID_RENDER_SHOWORBITS, Renderer::PlanetOrbits);
|
|
break;
|
|
|
|
case ID_RENDER_AMBIENTLIGHT_NONE:
|
|
CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_NONE, MF_CHECKED);
|
|
CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_LOW, MF_UNCHECKED);
|
|
CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_MEDIUM, MF_UNCHECKED);
|
|
renderer->setAmbientLightLevel(0.0f);
|
|
break;
|
|
case ID_RENDER_AMBIENTLIGHT_LOW:
|
|
CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_NONE, MF_UNCHECKED);
|
|
CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_LOW, MF_CHECKED);
|
|
CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_MEDIUM, MF_UNCHECKED);
|
|
renderer->setAmbientLightLevel(0.1f);
|
|
break;
|
|
case ID_RENDER_AMBIENTLIGHT_MEDIUM:
|
|
CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_NONE, MF_UNCHECKED);
|
|
CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_LOW, MF_UNCHECKED);
|
|
CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_MEDIUM, MF_CHECKED);
|
|
renderer->setAmbientLightLevel(0.25f);
|
|
break;
|
|
|
|
case ID_TIME_FASTER:
|
|
sim->setTimeScale(10.0 * sim->getTimeScale());
|
|
break;
|
|
case ID_TIME_SLOWER:
|
|
sim->setTimeScale(0.1 * sim->getTimeScale());
|
|
break;
|
|
case ID_TIME_REALTIME:
|
|
sim->setTimeScale(1.0);
|
|
break;
|
|
case ID_TIME_FREEZE:
|
|
if (paused)
|
|
{
|
|
sim->setTimeScale(timeScale);
|
|
CheckMenuItem(menuBar, ID_TIME_FREEZE, MF_UNCHECKED);
|
|
}
|
|
else
|
|
{
|
|
timeScale = sim->getTimeScale();
|
|
sim->setTimeScale(0.0);
|
|
CheckMenuItem(menuBar, ID_TIME_FREEZE, MF_CHECKED);
|
|
}
|
|
paused = !paused;
|
|
break;
|
|
case ID_TIME_SETTIME:
|
|
DialogBox(appInstance, MAKEINTRESOURCE(IDD_SETTIME), hWnd, SetTimeProc);
|
|
break;
|
|
|
|
case ID_HELP_ABOUT:
|
|
DialogBox(appInstance, MAKEINTRESOURCE(IDD_ABOUT), hWnd, AboutProc);
|
|
break;
|
|
|
|
case ID_HELP_LICENSE:
|
|
DialogBox(appInstance, MAKEINTRESOURCE(IDD_LICENSE), hWnd, LicenseProc);
|
|
break;
|
|
|
|
case ID_FILE_EXIT:
|
|
DestroyWindow(hWnd);
|
|
break;
|
|
|
|
default:
|
|
printf("Unhandled command: %d\n", LOWORD(wParam) - MENU_CHOOSE_PLANET);
|
|
if (LOWORD(wParam) >= MENU_CHOOSE_PLANET &&
|
|
LOWORD(wParam) < MENU_CHOOSE_PLANET + 1000)
|
|
{
|
|
sim->selectPlanet(LOWORD(wParam) - MENU_CHOOSE_PLANET);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_KEYUP:
|
|
handleKey(wParam, false);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
wglMakeCurrent(hDC, NULL);
|
|
wglDeleteContext(hRC);
|
|
if (fullscreen)
|
|
RestoreDisplayMode();
|
|
PostQuitMessage( 0 );
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
g_w = LOWORD(lParam);
|
|
g_h = HIWORD(lParam);
|
|
ChangeSize(g_w, g_h);
|
|
break;
|
|
|
|
case WM_PAINT:
|
|
if (bReady) {
|
|
// Get the current time, and update the time controller.
|
|
QueryPerformanceCounter(&TimeCur);
|
|
float fOldTime = fTime;
|
|
fTime = (float)((double)(TimeCur.QuadPart-TimeStart.QuadPart)/(double)TimerFreq.QuadPart);
|
|
fDeltaTime = fTime - fOldTime;
|
|
|
|
Quatf q(1);
|
|
if (leftPress)
|
|
q.zrotate(fDeltaTime * 2);
|
|
if (rightPress)
|
|
q.zrotate(fDeltaTime * -2);
|
|
if (downPress)
|
|
q.xrotate(fDeltaTime * 2);
|
|
if (upPress)
|
|
q.xrotate(fDeltaTime * -2);
|
|
sim->setOrientation(sim->getOrientation() * q);
|
|
position = Point3f(0, 0, distanceFromCenter) * conjugate(sim->getOrientation()).toMatrix4();
|
|
|
|
velocity *= 0.9f;
|
|
|
|
// cap the time step at 0.05 secs--extremely long time steps
|
|
// may make the simulation unstable
|
|
if (fDeltaTime > 0.05f)
|
|
fDeltaTime = 0.05f;
|
|
sim->update((double) fDeltaTime);
|
|
sim->render(*renderer);
|
|
SwapBuffers(hDC);
|
|
|
|
ValidateRect(hWnd, NULL);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return DefWindowProc( hWnd, uMsg, wParam, lParam );
|
|
}
|
|
|
|
return 0;
|
|
}
|