/*
Copyright (c) 2013 yvt
This file is part of OpenSpades.
OpenSpades 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 3 of the License, or
(at your option) any later version.
OpenSpades is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OpenSpades. If not, see .
*/
#include
#include "MainWindow.h"
#include
#include "../Core/Debug.h"
#include "SDLRunner.h"
#include
#include "../Core/Settings.h"
#include "../Imports/SDL.h"
#include
#include "../Core/FileManager.h"
#include "../Core/IStream.h"
#include "SDLAsyncRunner.h"
#include "DetailConfigWindow.h"
#include "../Core/Math.h"
#include "Serverbrowser.h"
#include "ErrorDialog.h"
#include "../Imports/OpenGL.h" //for gpu info
using namespace spades::gui;
SPADES_SETTING(cg_smp, "0");
SPADES_SETTING(cg_blood, "1");
SPADES_SETTING(cg_lastQuickConnectHost, "127.0.0.1");
SPADES_SETTING(cg_playerName, "Deuce");
SPADES_SETTING(r_bloom, "1");
SPADES_SETTING(r_lens, "1");
SPADES_SETTING(r_cameraBlur, "1");
SPADES_SETTING(r_softParticles, "1");
SPADES_SETTING(r_mapSoftShadow, "0");
SPADES_SETTING(r_modelShadows, "1");
SPADES_SETTING(r_radiosity, "0");
SPADES_SETTING(r_dlights, "1");
SPADES_SETTING(r_water, "2");
SPADES_SETTING(r_multisamples, "0");
SPADES_SETTING(r_fxaa, "1");
SPADES_SETTING(r_depthBits, "24");
SPADES_SETTING(r_colorBits, "");
SPADES_SETTING(r_videoWidth, "1024");
SPADES_SETTING(r_videoHeight, "640");
SPADES_SETTING(r_fullscreen, "0");
SPADES_SETTING(r_fogShadow, "0");
SPADES_SETTING(r_lensFlare, "1");
SPADES_SETTING(r_blitFramebuffer, "1");
SPADES_SETTING(r_srgb, "");
SPADES_SETTING(r_shadowMapSize, "2048");
SPADES_SETTING(s_maxPolyphonics, "96");
SPADES_SETTING(s_eax, "1");
SPADES_SETTING(r_maxAnisotropy, "8");
SPADES_SETTING(r_colorCorrection, "1");
SPADES_SETTING(r_physicalLighting, "");
static std::vector g_modes;
MainWindow::~MainWindow()
{
if( browser ) {
if( browser->IsAlive() ) {
browser->stopReading();
browser->Join();
}
delete browser;
}
}
void MainWindow::StartGame(const std::string &host) {
SPADES_MARK_FUNCTION();
hide();
#if 0
SDLRunner r(host);
r.Run();
#else
std::string err;
try{
if(cg_smp){
SDLAsyncRunner r(host, cg_playerName);
r.Run();
}else{
SDLRunner r(host, cg_playerName);
r.Run();
}
}catch(const spades::Exception& ex){
err = ex.GetShortMessage();
SPLog("Unhandled exception in SDLRunner:\n%s", ex.what());
}catch(const std::exception& ex){
err = ex.what();
SPLog("Unhandled exception in SDLRunner:\n%s", ex.what());
}
if(!err.empty()){
ErrorDialog dlg;
dlg.set_modal();
dlg.result = 0;
// TODO: free this buffer (just leaking)
Fl_Text_Buffer *buf = new Fl_Text_Buffer;
buf->append(err.c_str());
dlg.infoView->wrap_mode(Fl_Text_Display::WRAP_AT_BOUNDS, 0);
dlg.infoView->buffer(buf);
dlg.helpView->value("See SystemMessages.log for more details.");
dlg.show();
while(dlg.visible()){
Fl::wait();
}
if( dlg.result == 1 ){
//show();
}
}
#endif
}
void MainWindow::QuickConnectPressed() {
SPADES_MARK_FUNCTION();
StartGame(quickHostInput->value());
}
#pragma mark - Setup
void MainWindow::LoadPrefs() {
SPADES_MARK_FUNCTION();
SPLog("Loading Preferences to MainWindow");
// --- video
// add modes
char buf[64];
modeSelect->clear();
for(size_t i = 0; i < g_modes.size(); i++){
spades::IntVector3 mode = g_modes[i];
sprintf(buf, "%dx%d", mode.x, mode.y);
modeSelect->add(buf);
}
sprintf(buf, "%dx%d", (int)r_videoWidth, (int)r_videoHeight);
modeSelect->value(buf);
msaaSelect->clear();
if(r_blitFramebuffer) {
msaaSelect->add("Off");
msaaSelect->add("MSAA 2x");
msaaSelect->add("MSAA 4x");
msaaSelect->add("FXAA");
msaaSelect->add("Custom");
if(r_fxaa) {
if(r_multisamples){
msaaSelect->value(4);
}else{
msaaSelect->value(3);
}
}else{
switch((int)r_multisamples){
case 0:
case 1:
default:
msaaSelect->value(0);
break;
case 2:
msaaSelect->value(1);
break;
case 4:
msaaSelect->value(2);
break;
}
}
}else {
// MSAA is not supported with r_blitFramebuffer = 0
msaaSelect->add("Off");
msaaSelect->add("FXAA");
msaaSelect->add("Custom");
if(r_fxaa) {
if(r_multisamples){
msaaSelect->value(2);
}else{
msaaSelect->value(1);
}
}else{
switch((int)r_multisamples){
case 0:
case 1:
default:
msaaSelect->value(0);
break;
case 2:
case 4:
msaaSelect->value(2);
break;
}
}
}
quickHostInput->value(cg_lastQuickConnectHost.CString());
fullscreenCheck->value(r_fullscreen ? 1 : 0);
// --- graphics
if(r_cameraBlur && r_bloom && r_lens && r_lensFlare &&
r_colorCorrection) {
advancedLensCheck->value(1);
}else{
advancedLensCheck->value(0);
}
softParticleCheck->value(r_softParticles ? 1 : 0);
radiosityCheck->value(r_radiosity ? 1 : 0);
bloodCheck->value(cg_blood ? 1 : 0);
directLightSelect->clear();
directLightSelect->add("Low");
directLightSelect->add("Medium");
directLightSelect->add("High");
directLightSelect->add("Custom");
if((!r_mapSoftShadow) && (r_dlights) && (!r_modelShadows) && (!r_fogShadow) && (!r_physicalLighting)){
directLightSelect->value(0);
}else if((!r_mapSoftShadow) && (r_dlights) && (r_modelShadows) && (!r_fogShadow) && (!r_physicalLighting)){
directLightSelect->value(1);
}else if((r_mapSoftShadow) && (r_dlights) && (r_modelShadows) && (r_fogShadow) && (r_physicalLighting)){
directLightSelect->value(2);
}else{
directLightSelect->value(3);
}
shaderSelect->clear();
shaderSelect->add("Low");
shaderSelect->add("Medium");
if(shaderHighCapable){
shaderSelect->add("High");
}
shaderSelect->add("Custom");
if(shaderHighCapable){
if((!r_water)){
shaderSelect->value(0);
}else if((int)r_water == 1){
shaderSelect->value(1);
}else if((int)r_water == 2){
shaderSelect->value(2);
}else{
shaderSelect->value(3);
}
}else{
if((!r_water)){
shaderSelect->value(0);
}else if((int)r_water == 1){
shaderSelect->value(1);
}else{
shaderSelect->value(2);
}
}
// --- audio
polyInput->step(16.);
polyInput->range(32., 256.);
polyInput->value((int)s_maxPolyphonics);
eaxCheck->value(s_eax ? 1 : 0);
// --- game
playerNameInput->value(cg_playerName.CString());
playerNameInput->maximum_size(15);
}
void MainWindow::Init() {
SPADES_MARK_FUNCTION();
// banner
std::string data = spades::FileManager::ReadAllBytes("Gfx/Banner.png");
Fl_PNG_Image *img = new Fl_PNG_Image("Gfx/Banner.png", (const unsigned char *)data.data(), data.size());
bannerBox->image(img);
// --- about
std::string text, pkg;
pkg = PACKAGE_STRING;
text = std::string((const char *)aboutText, sizeof(aboutText));
text = spades::Replace(text, "${PACKAGE_STRING}",
pkg);
aboutView->value(text.c_str());
browser = new spades::Serverbrowser( serverListbox );
spades::ServerFilter::Flags flags = browser->Filter();
checkFilterEmpty->value( flags & spades::ServerFilter::flt_Empty );
checkFilterFull->value( flags & spades::ServerFilter::flt_Full );
checkFilterV75->value( flags & spades::ServerFilter::flt_Ver075 );
checkFilterV76->value( flags & spades::ServerFilter::flt_Ver076 );
checkFilterVOther->value( flags & spades::ServerFilter::flt_VerOther );
browser->Start();
}
/** This function is called after showing window.
* Creating SDL window before showing MainWindow results in
* internal error on Mac OS X. */
void MainWindow::CheckGLCapability() {
SPADES_MARK_FUNCTION();
// check GL capabilities
SPLog("Initializing SDL for capability query");
SDL_Init(SDL_INIT_VIDEO);
SDL_Rect **modes = SDL_ListModes(NULL, SDL_OPENGL | SDL_FULLSCREEN |
SDL_DOUBLEBUF);
if(modes && modes != (SDL_Rect **)-1){
g_modes.clear();
for(size_t i = 0; modes[i]; i++){
SDL_Rect mode = *(modes[i]);
if(mode.w < 800 || mode.h < 600)
continue;
g_modes.push_back(spades::IntVector3::Make(mode.w, mode.h, 0));
SPLog("Video Mode Found: %dx%d", mode.w, mode.h);
}
}
bool capable = true;
std::string msg;
if(!SDL_SetVideoMode(1,1, 32, SDL_OPENGL|SDL_NOFRAME)){
// OpenGL initialization failed!
outputGLRenderer->value("N/A");
outputGLVersion->value("N/A");
outputGLSLVersion->value("N/A");
std::string err = SDL_GetError();
SPLog("SDL_SetVideoMode failed: %s", err.c_str());
msg = "OpenGL/SDL couldn't be initialized. "
"You cannot play OpenSpades.
"
"Message from SDL
"
+ err;
capable = false;
shaderHighCapable = false;
}else{
shaderHighCapable = true;
const char *str;
GLint maxTextureSize;
GLint max3DTextureSize;
GLint maxCombinedTextureUnits;
GLint maxVertexTextureUnits;
SPLog("--- OpenGL Renderer Info ---");
if((str = (const char*)glGetString(GL_VENDOR)) != NULL) {
SPLog("Vendor: %s", str);
}
if((str = (const char *)glGetString(GL_RENDERER)) != NULL) {
outputGLRenderer->value(str);
SPLog("Name: %s", str);
}else{
outputGLRenderer->value("(unknown)");
}
if((str = (const char *)glGetString(GL_VERSION)) != NULL) {
outputGLVersion->value(str);
SPLog("Version: %s", str);
}else{
outputGLVersion->value("(unknown)");
}
if((str = (const char *)glGetString(GL_SHADING_LANGUAGE_VERSION)) != NULL) {
outputGLSLVersion->value(str);
SPLog("Shading Language Version: %s", str);
}else{
outputGLSLVersion->value("(unknown)");
}
maxTextureSize = 0;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
if(maxTextureSize > 0) {
SPLog("Max Texture Size: %d", (int)maxTextureSize);
}
max3DTextureSize = 0;
glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &max3DTextureSize);
if(max3DTextureSize > 0) {
SPLog("Max 3D Texture Size: %d", (int)max3DTextureSize);
}
maxCombinedTextureUnits = 0;
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxCombinedTextureUnits);
if(maxCombinedTextureUnits > 0) {
SPLog("Max Combined Texture Image Units: %d", (int)maxCombinedTextureUnits);
}
maxVertexTextureUnits = 0;
glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextureUnits);
if(maxVertexTextureUnits > 0) {
SPLog("Max Vertex Texture Image Units: %d", (int)maxVertexTextureUnits);
}
str = (const char*)glGetString(GL_EXTENSIONS);
std::string extensions;
if(str)
extensions = str;
const char * const requiredExtensions[] = {
"GL_ARB_multitexture",
"GL_ARB_shader_objects",
"GL_ARB_shading_language_100",
"GL_ARB_texture_non_power_of_two",
"GL_ARB_vertex_buffer_object",
"GL_EXT_framebuffer_object",
NULL
};
SPLog("--- Extensions ---");
std::vector strs = spades::Split(str, " ");
for(size_t i = 0; i < strs.size(); i++)
SPLog("%s", strs[i].c_str());
SPLog("------------------");
for(size_t i = 0; requiredExtensions[i]; i++) {
const char *ex = requiredExtensions[i];
if(extensions.find(ex) == std::string::npos) {
// extension not found
msg += "";
msg += ex;
msg += " NOT SUPPORTED!";
msg += "";
capable = false;
}else{
msg += "";
msg += ex;
msg += " is supported";
msg += "";
}
msg += "
";
}
msg += "
";
msg += "Other Extensions:
";
// non-requred extensions
if(extensions.find("GL_ARB_framebuffer_sRGB") ==
std::string::npos) {
if(r_srgb) {
r_srgb = 0;
SPLog("Disabling r_srgb: no GL_ARB_framebuffer_sRGB");
}
msg += "GL_ARB_framebuffer_sRGB is NOT SUPPORTED
";
msg += " r_srgb is disabled.
";
}else{
msg += "";
msg += "GL_ARB_framebuffer_sRGB is supported";
msg += "
";
}
if(extensions.find("GL_EXT_framebuffer_blit") ==
std::string::npos) {
if(r_blitFramebuffer) {
r_blitFramebuffer = 0;
SPLog("Disabling r_blitFramebuffer: no GL_EXT_framebuffer_blit");
}
if(r_multisamples) {
r_multisamples = 0;
SPLog("Disabling r_multisamples: no GL_EXT_framebuffer_blit");
}
msg += "GL_EXT_framebuffer_blit is NOT SUPPORTED
";
msg += " MSAA is disabled.
";
msg += " r_blitFramebuffer is disabled.
";
}else{
msg += "";
msg += "GL_EXT_framebuffer_blit is supported";
msg += "
";
}
if(extensions.find("GL_EXT_texture_filter_anisotropic") ==
std::string::npos) {
if((float)r_maxAnisotropy > 1.1f) {
r_maxAnisotropy = 1;
SPLog("Setting r_maxAnisotropy to 1: no GL_EXT_texture_filter_anisotropic");
}
msg += "GL_EXT_texture_filter_anisotropic is NOT SUPPORTED
";
msg += " r_maxAnisotropy is disabled.
";
}else{
msg += "";
msg += "GL_EXT_texture_filter_anisotropic is supported";
msg += "
";
}
msg += "
";
msg += "Miscellaneous:
";
char buf[256];
sprintf(buf, "Max Texture Size: %d
", (int)maxTextureSize);
msg += buf;
if(maxTextureSize < 1024) {
capable = false;
msg += "";
msg += " TOO SMALL (1024 required)";
msg += "
";
}
if((int)r_shadowMapSize > maxTextureSize) {
SPLog("Changed r_shadowMapSize from %d to %d: too small GL_MAX_TEXTURE_SIZE", (int)r_shadowMapSize, maxTextureSize);
r_shadowMapSize = maxTextureSize;
}
sprintf(buf, "Max 3D Texture Size: %d
", (int)max3DTextureSize);
msg += buf;
if(max3DTextureSize < 512) {
msg += " Global Illumation is disabled (512 required)
";
if(r_radiosity) {
r_radiosity = 0;
SPLog("Disabling r_radiosity: too small GL_MAX_3D_TEXTURE_SIZE");
radiosityCheck->deactivate();
}
}
sprintf(buf, "Max Combined Texture Image Units: %d
", (int)maxCombinedTextureUnits);
msg += buf;
if(maxCombinedTextureUnits < 12) {
msg += " Global Illumation is disabled (12 required)
";
if(r_radiosity) {
r_radiosity = 0;
SPLog("Disabling r_radiosity: too small GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS");
radiosityCheck->deactivate();
}
}
sprintf(buf, "Max Vertex Texture Image Units: %d
", (int)maxVertexTextureUnits);
msg += buf;
if(maxVertexTextureUnits < 3) {
msg += " Water 2 is disabled (3 required)
";
msg += " (Shader Effects is limited to Medium)
";
shaderHighCapable = false;
if((int)r_water >= 2) {
r_water = 1;
SPLog("Disabling Water 2: too small GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS");
}
}
if(capable){
msg = "Your video card supports all "
"required OpenGL extensions/features.
" + msg;
}else{
msg = "Your video card/driver doesn't support "
"at least one of required OpenGL extensions/features."
" You cannot play OpenSpades.
" + msg;
}
}
msg = "" + msg + "";
glInfoView->value(msg.c_str());
SDL_QuitSubSystem(SDL_INIT_VIDEO);
SPLog("SDL video subsystem finalized");
SPLog("System is OpenSpades capable: %s",
capable ? "YES": "NO");
if(!capable) {
mainTab->value(groupReport);
connectButton->deactivate();
}
LoadPrefs();
inited = true;
}
void MainWindow::SavePrefs() {
SPADES_MARK_FUNCTION();
if(!inited)
return;
std::string modeStr = modeSelect->value();
size_t pos = modeStr.find('x');
if(pos != std::string::npos){
int w = atoi(modeStr.substr(0, pos).c_str());
int h = atoi(modeStr.substr(pos + 1).c_str());
if(w >= 256 && h >= 256){
r_videoWidth = w;
r_videoHeight = h;
}
}
cg_lastQuickConnectHost = quickHostInput->value();
r_fullscreen = fullscreenCheck->value() ? 1 : 0;
switch(msaaSelect->value()){
case 0: r_multisamples = 0; r_fxaa = 0; break;
case 1: r_multisamples = 2; r_fxaa = 0; break;
case 2: r_multisamples = 4; r_fxaa = 0; break;
case 3: r_multisamples = 0; r_fxaa = 1; break;
}
// --- graphics
cg_blood = bloodCheck->value() ? 1 : 0;
r_bloom = advancedLensCheck->value() ? 1 : 0;
r_lens = advancedLensCheck->value() ? 1 : 0;
r_lensFlare = advancedLensCheck->value() ? 1 : 0;
r_cameraBlur = advancedLensCheck->value() ? 1 : 0;
r_colorCorrection = advancedLensCheck->value() ? 1 : 0;
r_softParticles = softParticleCheck->value() ? 1 : 0;
r_radiosity = radiosityCheck->value() ? 1 : 0;
switch(directLightSelect->value()){
case 0:
r_modelShadows = 0;
r_dlights = 1;
r_mapSoftShadow = 0;
r_fogShadow = 0;
r_physicalLighting = 0;
break;
case 1:
r_modelShadows = 1;
r_dlights = 1;
r_mapSoftShadow = 0;
r_fogShadow = 0;
r_physicalLighting = 0;
break;
case 2:
r_modelShadows = 1;
r_dlights = 1;
r_mapSoftShadow = 1;
r_fogShadow = 1;
r_physicalLighting = 1;
break;
}
if(shaderHighCapable){
switch(shaderSelect->value()){
case 0:
r_water = 0;
break;
case 1:
r_water = 1;
break;
case 2:
r_water = 2;
break;
}
}else{
switch(shaderSelect->value()){
case 0:
r_water = 0;
break;
case 1:
r_water = 1;
break;
}
}
// --- audio
s_maxPolyphonics = (int)polyInput->value();
s_eax = eaxCheck->value() ? 1 : 0;
// --- game
cg_playerName = playerNameInput->value();
}
void MainWindow::DisableMSAA() {
if(r_blitFramebuffer){
if(msaaSelect->value() >= 1 && msaaSelect->value() <= 2)
msaaSelect->value(3);
}
}
void MainWindow::MSAAEnabled() {
if(msaaSelect->value() >= 1 &&
msaaSelect->value() <= 2 &&
r_blitFramebuffer){
if(shaderSelect->value() == 1)
shaderSelect->value(0);
if(directLightSelect->value() == 2)
directLightSelect->value(1);
}
}
void MainWindow::OpenDetailConfig() {
SPADES_MARK_FUNCTION();
DetailConfigWindow cfg;
cfg.set_modal();
cfg.Init();
cfg.show();
while(cfg.visible()){
Fl::wait();
}
LoadPrefs();
}
void MainWindow::ServerSelectionChanged()
{
SPADES_MARK_FUNCTION();
if( browser ) {
int item = serverListbox->value();
if( item > 1 ) {
browser->onSelection( serverListbox->data( item ), quickHostInput );
} else if( item == 1 ) {
browser->onHeaderClick( Fl::event_x() - serverListbox->x() );
}
}
}
void MainWindow::updateFilters()
{
if( browser ) {
spades::ServerFilter::Flags flags = spades::ServerFilter::flt_None;
if( checkFilterEmpty->value() ) {
flags |= spades::ServerFilter::flt_Empty;
}
if( checkFilterFull->value() ) {
flags |= spades::ServerFilter::flt_Full;
}
if( checkFilterV75->value() ) {
flags |= spades::ServerFilter::flt_Ver075;
}
if( checkFilterV76->value() ) {
flags |= spades::ServerFilter::flt_Ver076;
}
if( checkFilterVOther->value() ) {
flags |= spades::ServerFilter::flt_VerOther;
}
browser->setFilter( flags );
browser->refreshList();
}
}