-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.cpp
350 lines (285 loc) · 11.9 KB
/
main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
//
// main.cpp
// SFML OGL
//
// Created by Jarvik7 on 2/11/14.
// Copyright (c) 2014 Jarvik7. All rights reserved.
//
#include <iostream> // std::cout
#include <string> // std::string
#include <stack>
#include <GLEW/glew.h> // For OpenGL Extensions
#include <SFML/OpenGL.hpp> // For OpenGL functions
#include <SFML/Graphics.hpp> // For SFML functions (window handling, ttf text drawing, vectors)
#include <SFML/Audio.hpp> // For MP3 playback ::TODO:: switch to something that supports MP3
#include "q3bsploader.h" // My BSP loader
#include "j7util.hpp" // My helper utility
#include <glm/gtc/type_ptr.hpp>
// Enable to display debug output ::TODO:: change this to read project build settings?
//const bool DISPLAYDEBUGOUTPUT = true;
// Define our key mapping ::TODO:: make this remappable in-game
const sf::Keyboard::Key key_quit = sf::Keyboard::Escape;
const sf::Keyboard::Key key_toggle_music = sf::Keyboard::M;
const sf::Keyboard::Key key_toggle_fullscreen = sf::Keyboard::F;
const sf::Keyboard::Key key_toggle_vsync = sf::Keyboard::V;
const sf::Keyboard::Key key_toggle_fps = sf::Keyboard::Tab;
const sf::Keyboard::Key key_toggle_blending = sf::Keyboard::Num4;
const sf::Keyboard::Key key_toggle_fog = sf::Keyboard::K;
const sf::Keyboard::Key key_toggle_culling = sf::Keyboard::C;
const sf::Keyboard::Key key_toggle_wireframe = sf::Keyboard::Num1;
const sf::Keyboard::Key key_toggle_texturing = sf::Keyboard::Num2;
const sf::Keyboard::Key key_toggle_lighting = sf::Keyboard::Num3;
const sf::Keyboard::Key key_lock_mouse = sf::Keyboard::L;
const sf::Keyboard::Key key_respawn = sf::Keyboard::T;
const sf::Keyboard::Key key_printloc = sf::Keyboard::Y;
GLuint shaderID;
bool initGL()
{
//Setup OpenGL backface culling
glFrontFace(GL_CW); // Quake3 uses CW for frontface
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
//Setup OpenGL depth buffer
glDepthRange(0, 1);
glClearDepth(1.0f);
glDepthMask(GL_TRUE);
glDepthFunc(GL_LEQUAL); // FIXME: Any reason not to use GL_LESS?
glEnable(GL_DEPTH_TEST);
//Enable multisampling AA
glEnable(GL_MULTISAMPLE_ARB);
//glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST); // Causes a GLERROR on win32. Obsolete according to Apple
//OpenGL quality hinting
//glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Immediate mode only?
glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
//glHint(GL_CLIP_VOLUME_CLIPPING_HINT_EXT, GL_FASTEST); // Disable HW frustrum culling as it should be faster in software (appears to have no effect?)
glewExperimental = true; // Needed for OSX to build?
if (glewInit()) {
std::cerr << "Error: Couldn't initialize GLEW.\n";
return false;
}
return true;
}
int main(const int argc, const char * argv[])
{
//Initialize the render window
const std::string windowTitle = "Q3 Map Viewer";
sf::ContextSettings windowsettings(24, 0, 16); // 24-bit depth buffer, 0-bit stencil, 16x MSAA, default GL version
sf::RenderWindow window(sf::VideoMode(800, 600, 32), windowTitle, sf::Style::Default, windowsettings);
if (!window.isOpen())
{
std::cerr << "Error: Couldn't create RenderWindow\n";
return EXIT_FAILURE;
}
// Initialize the OpenGL state
if (!initGL()) return EXIT_FAILURE; // Exit, GLew failed.
//Used for drawing tessellated surfaces
glPrimitiveRestartIndex(0xFFFFFFFF);
glEnable(GL_PRIMITIVE_RESTART);
//Setup Vsync (always ON on OSX)
bool vsync = true;
window.setVerticalSyncEnabled(vsync);
//Window settings
sf::Vector2u windowsize = window.getSize();
bool fullscreen = false;
//Mouse settings
bool hasFocus = true;
bool mouseWasLocked = false;
bool mouseLock = false;
if (mouseLock) sf::Mouse::setPosition(sf::Vector2i(windowsize.x / 2, windowsize.y / 2), window);
//Display debug info about graphics
if (DISPLAYDEBUGOUTPUT) {
windowsettings = window.getSettings();
const sf::Vector2i windowpos = window.getPosition();
std::cout << windowsize.x << "x" << windowsize.y << " window created at " << windowpos.x << "x" << windowpos.y << '\n';
std::cout << "OpenGL version: " << windowsettings.majorVersion << "." << windowsettings.minorVersion << '\n';
if (sf::Shader::isAvailable()) std::cout << "Shaders are available\n";
else std::cout << "Shaders are not available\n";
std::cout << "Depth bits: " << windowsettings.depthBits << '\n';
std::cout << "Stencil bits: " << windowsettings.stencilBits << '\n';
std::cout << "Antialiasing level: " << windowsettings.antialiasingLevel << '\n';
}
sf::Event event; //SFML event handler
// Animation control vars
bool showfps = true;
//Setup the vertex & fragment shaders
shaderID = glCreateProgram();
const GLenum vertshader = loadShader("texture.vert", GL_VERTEX_SHADER);
const GLenum fragshader = loadShader("texture.frag", GL_FRAGMENT_SHADER);
if (vertshader != 0 && fragshader != 0) {
glAttachShader(shaderID, vertshader);
glAttachShader(shaderID, fragshader);
glLinkProgram(shaderID);
GLint programSuccess;
glGetProgramiv(shaderID, GL_LINK_STATUS, &programSuccess);
if (programSuccess != GL_TRUE) {
std::cerr << "Error linking program: " << shaderID << ".\n";
glDeleteProgram(shaderID);
shaderID = 0;
}
}
else shaderID = 0;
glUseProgram(shaderID);
const GLint projectionViewLoc = glGetUniformLocation(shaderID, "projectionview");
const GLint modelViewLoc = glGetUniformLocation(shaderID, "modelview");
//Load map
q3BSP test("maps/q3dm1.bsp");
j7Model quake3(&test);
//Load map music
sf::Music music;
if (music.openFromFile(test.worldMusic))
{
music.setLoop(true);
music.setVolume(75);
music.play();
}
else std::cerr << "Could not open music file: " << test.worldMusic << ".\n";
//Setup camera
float fov = 75.0f;
j7Cam camera;
camera.adjustPerspective(windowsize, fov);
unsigned campos = 1;
camera.goTo(test.cameraPositions[campos].origin, test.cameraPositions[campos].angle); // FIXME: This is causing a breakpoint in debug for invalid index
//Begin game loop
GLenum glerror = GL_NO_ERROR;
bool gameover = false;
while (!gameover)
{
glerror = glGetError();
if (glerror != GL_NO_ERROR) std::cerr << "OpenGL ERROR: " << glerror << '\n';
glClear(/*GL_COLOR_BUFFER_BIT | */GL_DEPTH_BUFFER_BIT); // Clear depth buffer (color buffer clearing disabled = faster but smears when outside of map)
camera.update(&window);
//Setup view matrices
const glm::mat4 view = camera.modelviewMatrix.top() * glm::scale(glm::fvec3(1.0 / 255, 1.0 / 255, 1.0 / 255)); // Scale down the map ::TODO:: can this be done by adjusting our frustum or something?
glUniformMatrix4fv(modelViewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projectionViewLoc, 1, GL_FALSE, glm::value_ptr(camera.projectionMatrix.top()));
quake3.drawVBO(&test, camera.getCurrentPos(), camera.projectionMatrix.top() * view); // Render the BSP
//if (showfps) showFPS(&window); // Display the FPS, only works in compatibility profile
textFPS();
window.display();
//Handle window events
while (window.pollEvent(event))
{
switch (event.type)
{
case sf::Event::LostFocus:
hasFocus = false;
camera.setMouseLock(false, &window);
camera.setFocus(false);
break;
case sf::Event::GainedFocus:
if (mouseWasLocked) camera.setMouseLock(true, &window);
camera.setFocus(true);
hasFocus = true;
break;
case sf::Event::Closed:
gameover = true;
break;
case sf::Event::KeyPressed:
if (hasFocus) switch (event.key.code)
{
case key_quit:
gameover = true;
break;
case key_respawn:
++campos;
if (campos > test.cameraPositions.size() - 1) campos = 0;
camera.goTo(test.cameraPositions[campos].origin, test.cameraPositions[campos].angle);
break;
case key_printloc:
camera.printPos(&test);
break;
// Toggles
case key_lock_mouse:
{
mouseLock = !mouseLock;
mouseWasLocked = mouseLock;
camera.setMouseLock(mouseLock, &window);
break;
}
case key_toggle_culling:
if (!glIsEnabled(GL_CULL_FACE)) glEnable(GL_CULL_FACE);
else glDisable(GL_CULL_FACE);
break;
// These only work in fixed function OpenGL. Need to replace with a bool uniform sent to shader to turn on/off
/* case key_toggle_texturing:
if(!glIsEnabled(GL_TEXTURE_2D)) glEnable(GL_TEXTURE_2D);
else glDisable(GL_TEXTURE_2D);
break;
case key_toggle_lighting:
if(!glIsEnabled(GL_LIGHTING)) glEnable(GL_LIGHTING);
else glDisable(GL_LIGHTING);
break;*/
case key_toggle_wireframe:
GLint polygonMode;
glGetIntegerv(GL_POLYGON_MODE, &polygonMode);
if (polygonMode == GL_FILL) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
else glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
break;
case key_toggle_blending:
if(glIsEnabled(GL_DEPTH_TEST)) {
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
else {
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
}
break;
case key_toggle_fog:
if (!glIsEnabled(GL_FOG)) glEnable(GL_FOG);
else glDisable(GL_FOG);
break;
case key_toggle_fps:
showfps = !showfps;
break;
case key_toggle_music:
{
const sf::SoundSource::Status musicstatus = music.getStatus();
if (musicstatus == sf::SoundSource::Paused) music.play();
else if (musicstatus == sf::SoundSource::Playing) music.pause();
// The music should never be stopped unless it failed to load
break;
}
case key_toggle_fullscreen:
{
fullscreen = !fullscreen;
static sf::Vector2u oldwindowsize;
if (fullscreen)
{
oldwindowsize = windowsize;
window.create(sf::VideoMode::getDesktopMode(), windowTitle, sf::Style::Fullscreen, windowsettings);
}
else window.create(sf::VideoMode(oldwindowsize.x, oldwindowsize.y), windowTitle, sf::Style::Default, windowsettings);
initGL();
windowsize = window.getSize();
camera.adjustPerspective(windowsize, fov);
window.setVerticalSyncEnabled(vsync);
break;
}
case key_toggle_vsync:
vsync = !vsync;
window.setVerticalSyncEnabled(vsync);
break;
default:
break;
}
break;
case sf::Event::MouseWheelMoved: // Zoom
if (hasFocus) {
fov -= event.mouseWheel.delta * 1.5f;
camera.adjustPerspective(windowsize, fov);
}
break;
case sf::Event::Resized:
windowsize = window.getSize();
camera.adjustPerspective(windowsize, fov);
break;
default:
//std::cerr << "Unknown event type: " << event.type << std::endl;
break;
}
}
}
return EXIT_SUCCESS;
}