1 /** \file glHelp.cc
2 Some OpenGL utility algorithms.
3 */
4 /*
5 Copyright (C) 2000-2005 Mathias Broxvall
6 Yannick Perret
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23 #include "glHelp.h"
24
25 #include "font.h"
26 #include "game.h"
27 #include "map.h"
28 #include "settings.h"
29 #include "sparkle2d.h"
30
31 #include <SDL2/SDL_image.h>
32 #include <SDL2/SDL_ttf.h>
33 #include <cstdlib>
34 #include <map>
35
36 float fps = 50.0;
37 int screenWidth = 640, screenHeight = 480;
38 ViewParameters activeView;
39 UniformLocations uniformLocations;
40
41 static GLuint shaderWaterDay = 0;
42 static GLuint shaderWaterNight = 0;
43 static GLuint shaderTileDay = 0;
44 static GLuint shaderTileNight = 0;
45 static GLuint shaderTileShadow = 0;
46 static GLuint shaderLine = 0;
47 static GLuint shaderUI = 0;
48 static GLuint shaderObjectDay = 0;
49 static GLuint shaderObjectNight = 0;
50 static GLuint shaderObjectShadow = 0;
51 static GLuint shaderReflection = 0;
52 GLuint textureBlank = 0;
53 GLuint textureGlitter = 0;
54 GLuint textureMousePointer = 0;
55 GLuint textureDizzy = 0;
56 GLuint textureWings = 0;
57 GLuint textureTrack = 0;
58
59 TTF_Font *ingameFont;
60 extern struct timespec displayStartTime;
61 extern struct timespec lastDisplayStartTime;
62
63 const GLfloat menuColorSelected[4] = {0.86f, 0.86f, 0.86f, 1.f};
64 const GLfloat menuColor[4] = {0.86f, 0.86f, 0.25f, 1.f};
65
66 const double sin6[6] = {0.0, 0.8660254037844386, 0.8660254037844386,
67 0.0, -0.8660254037844386, -0.8660254037844386};
68 const double cos6[6] = {1.0, 0.5, -0.5, -1.0, -0.5, 0.5};
69 const double sin10[10] = {
70 0.0, 0.5877852522924731, 0.9510565162951535, 0.9510565162951535, 0.5877852522924731,
71 0.0, -0.5877852522924731, -0.9510565162951535, -0.9510565162951535, -0.5877852522924731};
72 const double cos10[10] = {
73 1.0, 0.8090169943749475, 0.30901699437494745, -0.30901699437494745, -0.8090169943749475,
74 -1.0, -0.8090169943749475, -0.30901699437494745, 0.30901699437494745, 0.8090169943749475};
75 const double sin12[12] = {0.0, 0.5, 0.8660254037844386, 1.0, 0.8660254037844386, 0.5,
76 0.0, -0.5, -0.8660254037844386, -1.0, -0.8660254037844386, -0.5};
77 const double cos12[12] = {1.0, 0.8660254037844386, 0.5, 0.0, -0.5, -0.8660254037844386,
78 -1.0, -0.8660254037844386, -0.5, 0.0, 0.5, 0.8660254037844386};
79 const double sin14[14] = {0.0,
80 0.4338837391175581,
81 0.7818314824680298,
82 0.9749279121818236,
83 0.9749279121818236,
84 0.7818314824680298,
85 0.4338837391175581,
86 0.0,
87 -0.4338837391175581,
88 -0.7818314824680298,
89 -0.9749279121818236,
90 -0.9749279121818236,
91 -0.7818314824680298,
92 -0.4338837391175581};
93 const double cos14[14] = {1.0,
94 0.9009688679024191,
95 0.6234898018587336,
96 0.22252093395631445,
97 -0.22252093395631445,
98 -0.6234898018587336,
99 -0.9009688679024191,
100 -1.0,
101 -0.9009688679024191,
102 -0.6234898018587336,
103 -0.22252093395631445,
104 0.22252093395631445,
105 0.6234898018587336,
106 0.9009688679024191};
107
108 #define GLHELP_MAX_TEXTURES 256
109 GLuint textures[GLHELP_MAX_TEXTURES] = {0}; // added init. to 0 (no texture)
110 char *textureNames[GLHELP_MAX_TEXTURES] = {NULL};
111 int numTextures;
112
113 #define MAX_BALL_DETAIL 32
114 GLfloat *sphere_points[MAX_BALL_DETAIL];
115 GLfloat *sphere_texcos[MAX_BALL_DETAIL];
116 ushort *sphere_idxs[MAX_BALL_DETAIL];
117
118 static std::map<int, TTF_Font *> menuFontLookup;
119
120 Sparkle2D *sparkle2D = NULL;
121
122 struct StringInfo {
123 TTF_Font *font;
124 char string[256];
125 };
operator <(const struct StringInfo & a,const struct StringInfo & b)126 static bool operator<(const struct StringInfo &a, const struct StringInfo &b) {
127 if (a.font != b.font) return a.font < b.font;
128 return strcmp(a.string, b.string) < 0;
129 }
130
131 struct StringCache {
132 GLuint texture;
133 GLfloat texcoord[4];
134 int w, h;
135 long tick;
136 };
137
138 static std::map<StringInfo, StringCache> strcache;
139
drawStringToSurface(struct StringInfo & inf,bool outlined)140 static SDL_Surface *drawStringToSurface(struct StringInfo &inf, bool outlined) {
141 SDL_Color white = {255, 255, 255, 255};
142 SDL_Color black = {0, 0, 0, 0};
143 if (outlined) {
144 TTF_SetFontOutline(inf.font, 0);
145
146 SDL_Surface *inner = TTF_RenderUTF8_Blended(inf.font, inf.string, white);
147 if (!inner) {
148 warning("Failed to render string outline '%s'", inf.string);
149 return NULL;
150 }
151 TTF_SetFontOutline(inf.font, 2);
152
153 SDL_Surface *outline = TTF_RenderUTF8_Blended(inf.font, inf.string, black);
154 if (!outline) {
155 warning("Failed to render string inside '%s'", inf.string);
156 return NULL;
157 }
158 SDL_Rect rect = {2, 2, inner->w, inner->h};
159 SDL_SetSurfaceBlendMode(inner, SDL_BLENDMODE_BLEND);
160 SDL_BlitSurface(inner, NULL, outline, &rect);
161 SDL_FreeSurface(inner);
162 return outline;
163 } else {
164 SDL_Surface *outline = TTF_RenderUTF8_Blended(inf.font, inf.string, white);
165 if (!outline) {
166 warning("Failed to render string '%s'", inf.string);
167 return NULL;
168 }
169 return outline;
170 }
171 }
172
draw2DString(TTF_Font * font,const char * string,int x,int y,float red,float green,float blue,float alpha,bool outlined,int align,int maxwidth)173 int draw2DString(TTF_Font *font, const char *string, int x, int y, float red, float green,
174 float blue, float alpha, bool outlined, int align, int maxwidth) {
175 struct StringInfo inf;
176 inf.font = font;
177 memset(inf.string, 0, 256);
178 strncpy(inf.string, string, 255);
179 inf.string[255] = '\0';
180 if (strcache.count(inf) <= 0) {
181 struct StringCache newentry;
182 newentry.tick = displayFrameNumber;
183 SDL_Surface *surf = drawStringToSurface(inf, outlined);
184 if (!surf) { return 0; }
185 newentry.texture = LoadTexture(surf, newentry.texcoord);
186 newentry.w = surf->w;
187 newentry.h = surf->h;
188 SDL_FreeSurface(surf);
189 strcache[inf] = newentry;
190 }
191
192 struct StringCache &cached = strcache[inf];
193 cached.tick = displayFrameNumber;
194
195 GLfloat shrink = (maxwidth > 0 && maxwidth < cached.w) ? (maxwidth / (GLfloat)cached.w) : 1.;
196 draw2DRectangle(x - shrink * align * cached.w / 2, y - shrink * cached.h / 2,
197 shrink * cached.w, shrink * cached.h, cached.texcoord[0], cached.texcoord[1],
198 cached.texcoord[2], cached.texcoord[3], red, green, blue, alpha,
199 cached.texture);
200 return maxwidth > 0 ? std::min(cached.w, maxwidth) : cached.w;
201 }
202
update2DStringCache(bool force_wipe)203 void update2DStringCache(bool force_wipe) {
204 int erased;
205 do {
206 erased = false;
207 // TODO: better asymptotics
208 for (std::map<StringInfo, StringCache>::iterator i = strcache.begin(); i != strcache.end();
209 ++i) {
210 if (i->second.tick < displayFrameNumber - 100 || force_wipe) {
211 glDeleteTextures(1, &i->second.texture);
212 strcache.erase(i);
213 erased = true;
214 break;
215 }
216 }
217 } while (erased);
218 }
219
menuFontForSize(int sz)220 TTF_Font *menuFontForSize(int sz) {
221 if (!menuFontLookup.count(sz)) {
222 char str[256];
223 snprintf(str, sizeof(str), "%s/fonts/%s", effectiveShareDir, "FreeSerifBoldItalic.ttf");
224 menuFontLookup[sz] = TTF_OpenFont(str, 2 * sz); // barbatri
225 if (!menuFontLookup[sz]) { error("failed to load font %s", str); }
226 }
227 return menuFontLookup[sz];
228 }
229
230 double mousePointerPhase = 0.0;
231
tickMouse(Real td)232 void tickMouse(Real td) {
233 int mouseX, mouseY;
234 static int oldMouseX = 0, oldMouseY = 0;
235 SDL_GetMouseState(&mouseX, &mouseY);
236 float mouseSpeedX = (mouseX - oldMouseX) / td;
237 float mouseSpeedY = (mouseY - oldMouseY) / td;
238 float mouseSpeedLen = std::sqrt(mouseSpeedX * mouseSpeedX + mouseSpeedY * mouseSpeedY);
239 if (mouseSpeedLen > 25.f) {
240 mouseSpeedX /= 25.f / mouseSpeedLen;
241 mouseSpeedY /= 25.f / mouseSpeedLen;
242 mouseSpeedLen = 25.f;
243 }
244 static Real last_sparkle = 0.0;
245
246 oldMouseX = mouseX;
247 oldMouseY = mouseY;
248
249 mousePointerPhase += td;
250 sparkle2D->tick(td);
251 last_sparkle += td * (1.0 + mouseSpeedLen * 0.1);
252 while (last_sparkle > 0.0) {
253 last_sparkle -= 0.05;
254 float pos[2] = {(float)(mouseX + 10.0f * (frandom() - 0.5f)),
255 (float)(mouseY + 10.0f * (frandom() - 0.5f))};
256 float speed[2] = {(float)(mouseSpeedX / 2.f + 100.0f * (frandom() - 0.5f)),
257 (float)(mouseSpeedY / 2.f - 120.0f * frandom())};
258 float color[4] = {(float)(0.8f + 0.2f * frandom()), (float)(0.8f + 0.2f * frandom()),
259 (float)(0.5f + 0.2f * frandom()), (float)(0.9f - 0.35f * frandom())};
260 sparkle2D->add(pos, speed, (float)(1.5 + 0.5 * (frandom() - 0.5)),
261 (float)(11 + 3. * (frandom() - 0.5)), color);
262 }
263 }
264
draw2DRectangle(GLfloat x,GLfloat y,GLfloat w,GLfloat h,GLfloat tx,GLfloat ty,GLfloat tw,GLfloat th,GLfloat r,GLfloat g,GLfloat b,GLfloat a,GLuint tex)265 void draw2DRectangle(GLfloat x, GLfloat y, GLfloat w, GLfloat h, GLfloat tx, GLfloat ty,
266 GLfloat tw, GLfloat th, GLfloat r, GLfloat g, GLfloat b, GLfloat a,
267 GLuint tex) {
268 GLfloat corners[4][2] = {{x, y}, {x, y + h}, {x + w, y}, {x + w, y + h}};
269 GLfloat texture[4][2] = {{tx, ty}, {tx, ty + th}, {tx + tw, ty}, {tx + tw, ty + th}};
270 GLfloat colors[4][4] = {{r, g, b, a}, {r, g, b, a}, {r, g, b, a}, {r, g, b, a}};
271 draw2DQuad(corners, texture, colors, tex);
272 }
273
draw2DQuad(const GLfloat ver[4][2],const GLfloat txc[4][2],const GLfloat col[4][4],GLuint tex)274 void draw2DQuad(const GLfloat ver[4][2], const GLfloat txc[4][2], const GLfloat col[4][4],
275 GLuint tex) {
276 Require2DMode();
277 if (tex == 0) { tex = textureBlank; }
278
279 const GLfloat data[32] = {
280 ver[0][0], ver[0][1], col[0][0], col[0][1], col[0][2], col[0][3], txc[0][0], txc[0][1],
281 ver[1][0], ver[1][1], col[1][0], col[1][1], col[1][2], col[1][3], txc[1][0], txc[1][1],
282 ver[2][0], ver[2][1], col[2][0], col[2][1], col[2][2], col[2][3], txc[2][0], txc[2][1],
283 ver[3][0], ver[3][1], col[3][0], col[3][1], col[3][2], col[3][3], txc[3][0], txc[3][1],
284 };
285
286 static GLuint idxs = (GLuint)-1;
287 static GLuint buf = (GLuint)-1;
288 static GLuint vao = (GLuint)-1;
289 if (idxs == (GLuint)-1) {
290 glGenBuffers(1, &idxs);
291 glGenBuffers(1, &buf);
292 glGenVertexArrays(1, &vao);
293
294 const ushort idxdata[6] = {0, 1, 2, 1, 2, 3};
295
296 glBindVertexArray(vao);
297 glBindBuffer(GL_ARRAY_BUFFER, buf);
298 glBufferData(GL_ARRAY_BUFFER, 32 * sizeof(GLfloat), NULL, GL_STREAM_DRAW);
299 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, idxs);
300 glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(ushort), idxdata, GL_STATIC_DRAW);
301 configureUIAttributes();
302 } else {
303 glBindVertexArray(vao);
304 }
305
306 glBindBuffer(GL_ARRAY_BUFFER, buf);
307 glBufferSubData(GL_ARRAY_BUFFER, 0, 32 * sizeof(GLfloat), data);
308
309 glBindTexture(GL_TEXTURE_2D, tex);
310 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (void *)0);
311 }
312
drawMousePointer()313 void drawMousePointer() {
314 int mouseX, mouseY;
315 sparkle2D->draw();
316 SDL_GetMouseState(&mouseX, &mouseY);
317 drawMouse(mouseX, mouseY, 64, 64);
318 }
319
drawMouse(int x,int y,int w,int h)320 void drawMouse(int x, int y, int w, int h) {
321 GLfloat r1 = 1.0 + 0.1 * std::cos(mousePointerPhase * 1.8);
322 GLfloat r2 = 1.0 + 0.1 * std::cos(mousePointerPhase * 1.9);
323 GLfloat dx = 0.707f * w * r1 * std::sin(mousePointerPhase * 0.35);
324 GLfloat dy = 0.707f * h * r2 * std::cos(mousePointerPhase * 0.35);
325
326 GLfloat colors[4][4] = {
327 {1., 1., 1., 1.}, {1., 1., 1., 1.}, {1., 1., 1., 1.}, {1., 1., 1., 1.}};
328 GLfloat texco[4][2] = {{0., 0.}, {0., 1.}, {1., 0.}, {1., 1.}};
329
330 GLfloat vco[4][2] = {{x - dx, y - dy}, {x - dy, y + dx}, {x + dy, y - dx}, {x + dx, y + dy}};
331 draw2DQuad(vco, texco, colors, textureMousePointer);
332 }
333
packObjectVertex(void * dest,GLfloat x,GLfloat y,GLfloat z,GLfloat tx,GLfloat ty,const Color & color,const GLfloat normal[3])334 size_t packObjectVertex(void *dest, GLfloat x, GLfloat y, GLfloat z, GLfloat tx, GLfloat ty,
335 const Color &color, const GLfloat normal[3]) {
336 uint32_t *aout = (uint32_t *)dest;
337 GLfloat *fout = (GLfloat *)dest;
338 fout[0] = x;
339 fout[1] = y;
340 fout[2] = z;
341 aout[3] = (((uint32_t)(color.v[1])) << 16) + (uint32_t)(color.v[0]);
342 aout[4] = (((uint32_t)(color.v[3])) << 16) + (uint32_t)(color.v[2]);
343 aout[5] = (((uint32_t)(65535.f * 0.5f * ty)) << 16) + (uint32_t)(65535.f * 0.5f * tx);
344 aout[6] = packNormal(normal);
345 return 8 * sizeof(GLfloat);
346 }
configureObjectAttributes()347 void configureObjectAttributes() {
348 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (void *)0);
349 glVertexAttribPointer(1, 4, GL_UNSIGNED_SHORT, GL_TRUE, 8 * sizeof(GLfloat),
350 (void *)(3 * sizeof(GLfloat)));
351 glVertexAttribPointer(2, 2, GL_UNSIGNED_SHORT, GL_TRUE, 8 * sizeof(GLfloat),
352 (void *)(5 * sizeof(GLfloat)));
353 glVertexAttribPointer(3, 4, GL_UNSIGNED_INT_2_10_10_10_REV, GL_TRUE, 8 * sizeof(GLfloat),
354 (void *)(6 * sizeof(GLfloat)));
355 glEnableVertexAttribArray(0);
356 glEnableVertexAttribArray(1);
357 glEnableVertexAttribArray(2);
358 glEnableVertexAttribArray(3);
359 }
configureUIAttributes()360 void configureUIAttributes() {
361 // To be used by `shaderUI`
362 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (void *)0);
363 glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat),
364 (void *)(2 * sizeof(GLfloat)));
365 glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat),
366 (void *)(6 * sizeof(GLfloat)));
367 glEnableVertexAttribArray(0);
368 glEnableVertexAttribArray(1);
369 glEnableVertexAttribArray(2);
370 }
371
372 static GLuint lastProgram = 0;
373
markViewChanged()374 void markViewChanged() { lastProgram = 0; }
375
setActiveProgramAndUniforms(Shader_Type type)376 void setActiveProgramAndUniforms(Shader_Type type) {
377 GLuint shader = 0;
378 switch (type) {
379 case Shader_Line:
380 shader = shaderLine;
381 break;
382 case Shader_Object:
383 if (activeView.calculating_shadows) {
384 shader = shaderObjectShadow;
385 } else {
386 if (activeView.day_mode) {
387 shader = shaderObjectDay;
388 } else {
389 shader = shaderObjectNight;
390 }
391 }
392 break;
393 case Shader_Tile:
394 if (activeView.calculating_shadows) {
395 shader = shaderTileShadow;
396 } else {
397 if (activeView.day_mode) {
398 shader = shaderTileDay;
399 } else {
400 shader = shaderTileNight;
401 }
402 }
403 break;
404 case Shader_Water:
405 if (activeView.day_mode) {
406 shader = shaderWaterDay;
407 } else {
408 shader = shaderWaterNight;
409 }
410 break;
411 case Shader_Reflection:
412 shader = shaderReflection;
413 break;
414 }
415
416 if (shader == lastProgram) return;
417 glUseProgram(shader);
418 lastProgram = shader;
419
420 /* Set universal uniforms */
421 Matrix4d mvp;
422 matrixMult(activeView.modelview, activeView.projection, mvp);
423 GLfloat lmvp[16], lmodel[16];
424 for (int i = 0; i < 4; i++) {
425 for (int j = 0; j < 4; j++) {
426 lmvp[4 * i + j] = mvp[i][j];
427 lmodel[4 * i + j] = activeView.modelview[i][j];
428 }
429 }
430 glUniformMatrix4fv(glGetUniformLocation(shader, "mvp_matrix"), 1, GL_FALSE, (GLfloat *)lmvp);
431 glUniformMatrix4fv(glGetUniformLocation(shader, "model_matrix"), 1, GL_FALSE,
432 (GLfloat *)lmodel);
433
434 switch (type) {
435 case Shader_Tile:
436 uniformLocations.render_stage = glGetUniformLocation(shader, "render_stage");
437 uniformLocations.arrtex = glGetUniformLocation(shader, "arrtex");
438 uniformLocations.gameTime = glGetUniformLocation(shader, "gameTime");
439 break;
440 case Shader_Water:
441 uniformLocations.wtex = glGetUniformLocation(shader, "wtex");
442 uniformLocations.gameTime = glGetUniformLocation(shader, "gameTime");
443 break;
444 case Shader_Object:
445 uniformLocations.render_stage = glGetUniformLocation(shader, "render_stage");
446 uniformLocations.specular_color = glGetUniformLocation(shader, "specular");
447 uniformLocations.sharpness = glGetUniformLocation(shader, "sharpness");
448 uniformLocations.use_lighting = glGetUniformLocation(shader, "use_lighting");
449 uniformLocations.ignore_shadow = glGetUniformLocation(shader, "ignore_shadow");
450 break;
451 case Shader_Reflection:
452 uniformLocations.refl_color = glGetUniformLocation(shader, "refl_color");
453 uniformLocations.tex = glGetUniformLocation(shader, "tex");
454 break;
455 case Shader_Line:
456 uniformLocations.line_color = glGetUniformLocation(shader, "line_color");
457 break;
458 }
459 if (!activeView.calculating_shadows) {
460 /* Fog applies to all display shaders */
461 glUniform1i(glGetUniformLocation(shader, "fog_active"), activeView.fog_enabled);
462 glUniform3f(glGetUniformLocation(shader, "fog_color"), activeView.fog_color[0],
463 activeView.fog_color[1], activeView.fog_color[2]);
464 glUniform1f(glGetUniformLocation(shader, "fog_start"), activeView.fog_start);
465 glUniform1f(glGetUniformLocation(shader, "fog_end"), activeView.fog_end);
466
467 if (type == Shader_Object || type == Shader_Water || type == Shader_Tile) {
468 glUniform3f(glGetUniformLocation(shader, "light_ambient"), activeView.light_ambient[0],
469 activeView.light_ambient[1], activeView.light_ambient[2]);
470 glUniform3f(glGetUniformLocation(shader, "light_diffuse"), activeView.light_diffuse[0],
471 activeView.light_diffuse[1], activeView.light_diffuse[2]);
472 if (type == Shader_Object) {
473 glUniform3f(glGetUniformLocation(shader, "light_specular"),
474 activeView.light_specular[0], activeView.light_specular[1],
475 activeView.light_specular[2]);
476 }
477
478 if (activeView.day_mode) {
479 glUniform1i(glGetUniformLocation(shader, "shadow_cascade0"), 2);
480 glActiveTexture(GL_TEXTURE2);
481 glBindTexture(GL_TEXTURE_2D, activeView.cascadeTexture[0]);
482 glUniform1i(glGetUniformLocation(shader, "shadow_cascade1"), 3);
483 glActiveTexture(GL_TEXTURE3);
484 glBindTexture(GL_TEXTURE_2D, activeView.cascadeTexture[1]);
485 glUniform1i(glGetUniformLocation(shader, "shadow_cascade2"), 4);
486 glActiveTexture(GL_TEXTURE4);
487 glBindTexture(GL_TEXTURE_2D, activeView.cascadeTexture[2]);
488
489 const int N = 3;
490 GLfloat cscmvp[N * 16], cscmodel[N * 16];
491 for (int i = 0; i < N; i++) {
492 Matrix4d cmvp;
493 matrixMult(activeView.cascade_model[i], activeView.cascade_proj[i], cmvp);
494 for (int j = 0; j < 4; j++) {
495 for (int k = 0; k < 4; k++) {
496 cscmvp[16 * i + 4 * j + k] = cmvp[j][k];
497 cscmodel[16 * i + 4 * j + k] = activeView.cascade_model[i][j][k];
498 }
499 }
500 }
501 glUniformMatrix4fv(glGetUniformLocation(shader, "cascade_mvp"), 3, GL_FALSE,
502 (GLfloat *)cscmvp);
503 glUniformMatrix4fv(glGetUniformLocation(shader, "cascade_model"), 3, GL_FALSE,
504 (GLfloat *)cscmodel);
505
506 glUniform3f(glGetUniformLocation(shader, "sun_direction"), activeView.sun_direction[0],
507 activeView.sun_direction[1], activeView.sun_direction[2]);
508 } else {
509 glUniform1i(glGetUniformLocation(shader, "shadow_map"), 1);
510 glActiveTexture(GL_TEXTURE1);
511 glBindTexture(GL_TEXTURE_CUBE_MAP, activeView.shadowMapTexture);
512
513 glUniform3f(glGetUniformLocation(shader, "light_position"),
514 activeView.light_position[0], activeView.light_position[1],
515 activeView.light_position[2]);
516 }
517 }
518 if (type != Shader_Line) { glActiveTexture(GL_TEXTURE0 + 0); }
519 }
520 }
setObjectUniforms(Color c,float sharpness,Object_Lighting lighting)521 void setObjectUniforms(Color c, float sharpness, Object_Lighting lighting) {
522 if (lastProgram == shaderObjectDay || lastProgram == shaderObjectNight) {
523 glUniformC(uniformLocations.specular_color, c);
524 glUniform1f(uniformLocations.sharpness, sharpness);
525 glUniform1i(uniformLocations.ignore_shadow, lighting == Lighting_Regular);
526 glUniform1i(uniformLocations.use_lighting, lighting != Lighting_None);
527 }
528 }
529
countObjectSpherePoints(int * ntriangles,int * nvertices,int detail)530 void countObjectSpherePoints(int *ntriangles, int *nvertices, int detail) {
531 if (detail < 1) {
532 warning("Sphere detail level must be > 1");
533 *ntriangles = 0.;
534 *nvertices = 0.;
535 return;
536 }
537
538 int radial_count = 4 * detail;
539 int nrows = 2 * detail + 1;
540 *nvertices = (radial_count + 1) * (nrows - 2) + 2;
541 *ntriangles = 2 * radial_count * (nrows - 2);
542 }
543
constructSphereData(int detail)544 static void constructSphereData(int detail) {
545 int radial_count = 4 * detail;
546 int nrows = 2 * detail + 1;
547 int nvertices = (radial_count + 1) * (nrows - 2) + 2;
548 int ntriangles = 2 * radial_count * (nrows - 2);
549
550 GLfloat *pts = new GLfloat[3 * nvertices];
551 GLfloat *txs = new GLfloat[2 * nvertices];
552 ushort *idxs = new ushort[3 * ntriangles];
553
554 // Construct vertices
555 int m = 0;
556 for (int z = 1; z < nrows - 1; z++) {
557 GLfloat theta = z * M_PI / (nrows - 1);
558 for (int t = 0; t <= radial_count; t++) {
559 GLfloat phi = 2 * t * M_PI / radial_count;
560 // Stagger every second row
561 if (z % 2 == 1) phi += M_PI / radial_count;
562 txs[2 * m + 0] = phi / (2 * M_PI);
563 txs[2 * m + 1] = theta / M_PI;
564 pts[3 * m + 0] = std::sin(theta) * std::cos(phi);
565 pts[3 * m + 1] = std::sin(theta) * std::sin(phi);
566 pts[3 * m + 2] = std::cos(theta);
567 m++;
568 }
569 }
570 pts[3 * m + 0] = 0;
571 pts[3 * m + 1] = 0;
572 pts[3 * m + 2] = 1.;
573 pts[3 * m + 3] = 0;
574 pts[3 * m + 4] = 0;
575 pts[3 * m + 5] = -1.;
576 txs[2 * m + 0] = 0.5;
577 txs[2 * m + 1] = 0.0;
578 txs[2 * m + 2] = 0.5;
579 txs[2 * m + 3] = 1.0;
580
581 // Triangulate end caps
582 for (int i = 0; i < radial_count; i++) {
583 idxs[6 * i + 0] = i;
584 idxs[6 * i + 1] = i + 1;
585 idxs[6 * i + 2] = (radial_count + 1) * (nrows - 2);
586 idxs[6 * i + 3] = (radial_count + 1) * (nrows - 3) + i + 1;
587 idxs[6 * i + 4] = (radial_count + 1) * (nrows - 3) + i;
588 idxs[6 * i + 5] = (radial_count + 1) * (nrows - 2) + 1;
589 }
590 // Triangulate body
591 ushort *base = &idxs[2 * 3 * radial_count];
592 for (int z = 1; z < nrows - 2; z++) {
593 for (int i = 0; i < radial_count; i++) {
594 base[6 * i + 0] = (z - 1) * (radial_count + 1) + i + 1;
595 base[6 * i + 1] = (z - 1) * (radial_count + 1) + i;
596 base[6 * i + 2] = z * (radial_count + 1) + i + 1;
597 base[6 * i + 3] = (z - 1) * (radial_count + 1) + i;
598 base[6 * i + 4] = z * (radial_count + 1) + i;
599 base[6 * i + 5] = z * (radial_count + 1) + i + 1;
600 }
601 base = &base[6 * radial_count];
602 }
603
604 sphere_points[detail] = pts;
605 sphere_texcos[detail] = txs;
606 sphere_idxs[detail] = idxs;
607 }
608
placeObjectSphere(void * data,ushort * idxs,ushort first_index,GLfloat const position[3],Matrix3d rotation,GLfloat radius,int detail,const Color & color)609 void placeObjectSphere(void *data, ushort *idxs, ushort first_index, GLfloat const position[3],
610 Matrix3d rotation, GLfloat radius, int detail, const Color &color) {
611 if (detail < 1) {
612 warning("Sphere detail level must be > 1. Drawing nothing.");
613 return;
614 }
615 if (!sphere_points[detail]) { constructSphereData(detail); }
616
617 int radial_count = 4 * detail;
618 int nrows = 2 * detail + 1;
619 int nvertices = (radial_count + 1) * (nrows - 2) + 2;
620 int ntriangles = 2 * radial_count * (nrows - 2);
621 for (int i = 0; i < 3 * ntriangles; i++) { idxs[i] = first_index + sphere_idxs[detail][i]; }
622
623 /* cast to float early */
624 GLfloat rot[3][3];
625 for (int i = 0; i < 3; i++)
626 for (int j = 0; j < 3; j++) rot[i][j] = rotation[i][j];
627
628 /* Copy and transform vertices */
629 char *pos = (char *)data;
630 GLfloat *pts = sphere_points[detail];
631 GLfloat *txs = sphere_texcos[detail];
632 for (int i = 0; i < nvertices; i++) {
633 GLfloat loc[3] = {pts[3 * i], pts[3 * i + 1], pts[3 * i + 2]};
634 GLfloat txc[2] = {txs[2 * i], txs[2 * i + 1]};
635
636 GLfloat off[3] = {0.f, 0.f, 0.f};
637 for (int j = 0; j < 3; j++)
638 for (int k = 0; k < 3; k++) off[j] += rot[j][k] * loc[k];
639 pos += packObjectVertex(pos, position[0] + radius * off[0], position[1] + radius * off[1],
640 position[2] + radius * off[2], txc[0], txc[1], color, off);
641 }
642 }
643
perspectiveMatrix(GLdouble fovy_deg,GLdouble aspect,GLdouble zNear,GLdouble zFar,Matrix4d out)644 void perspectiveMatrix(GLdouble fovy_deg, GLdouble aspect, GLdouble zNear, GLdouble zFar,
645 Matrix4d out) {
646 GLdouble f = 1. / std::tan(fovy_deg * M_PI / 360.0);
647 Matrix4d mat = {{f / aspect, 0., 0., 0.},
648 {0., f, 0., 0.},
649 {0., 0., (zFar + zNear) / (zNear - zFar), -1.},
650 {0., 0., 2 * zFar * zNear / (zNear - zFar), 0.}};
651 assign(mat, out);
652 }
653
lookAtMatrix(GLdouble eyeX,GLdouble eyeY,GLdouble eyeZ,GLdouble centerX,GLdouble centerY,GLdouble centerZ,GLdouble upX,GLdouble upY,GLdouble upZ,Matrix4d out)654 void lookAtMatrix(GLdouble eyeX, GLdouble eyeY, GLdouble eyeZ, GLdouble centerX,
655 GLdouble centerY, GLdouble centerZ, GLdouble upX, GLdouble upY, GLdouble upZ,
656 Matrix4d out) {
657 Coord3d eye(eyeX, eyeY, eyeZ);
658 Coord3d center(centerX, centerY, centerZ);
659 Coord3d up(upX, upY, upZ);
660 Coord3d f = center - eye;
661 f = f / length(f);
662 up = up / length(up);
663 Coord3d s = crossProduct(f, up);
664 s = s / length(s);
665 Coord3d u = crossProduct(s, f);
666
667 Matrix4d mat = {{s[0], u[0], -f[0], 0.},
668 {s[1], u[1], -f[1], 0.},
669 {s[2], u[2], -f[2], 0.},
670 {0., 0., 0., 1.}};
671 Matrix4d trans = {
672 {1., 0., 0., 0.}, {0., 1., 0., 0.}, {0., 0., 1., 0.}, {-eye[0], -eye[1], -eye[2], 1.}};
673 matrixMult(trans, mat, out);
674 }
675
renderDummyShadowMap()676 void renderDummyShadowMap() {
677 GLenum dirs[6] = {GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
678 GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
679 GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z};
680 GLfloat buf[1] = {1.0f};
681 activeView.shadowMapTexsize = 1;
682 glBindTexture(GL_TEXTURE_CUBE_MAP, activeView.shadowMapTexture);
683 for (int face = 0; face < 6; face++) {
684 glTexImage2D(dirs[face], 0, GL_DEPTH_COMPONENT, 1, 1, 0, GL_DEPTH_COMPONENT, GL_FLOAT,
685 buf);
686 }
687 }
688
renderDummyShadowCascade()689 void renderDummyShadowCascade() {
690 GLfloat buf[3] = {1.0f};
691 activeView.cascadeTexsize = 1;
692 for (int i = 0; i < 3; i++) {
693 glBindTexture(GL_TEXTURE_2D, activeView.cascadeTexture[i]);
694 glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 1, 1, 0, GL_DEPTH_COMPONENT, GL_FLOAT,
695 buf);
696 }
697 }
698
renderShadowMap(const Coord3d & focus,Map * mp,Game * gm)699 void renderShadowMap(const Coord3d &focus, Map *mp, Game *gm) {
700 Matrix4d origMV, origProj;
701 assign(activeView.modelview, origMV);
702 assign(activeView.projection, origProj);
703
704 activeView.calculating_shadows = true;
705 static int ltexsize = 0;
706 if (activeView.shadowMapTexsize <= 1 || ltexsize != Settings::settings->shadowTexsize) {
707 ltexsize = Settings::settings->shadowTexsize;
708 /* order doesn't matter */
709 GLenum dirs[6] = {GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
710 GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
711 GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z};
712
713 GLint maxSize;
714 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
715 GLint reqSize = 1 << Settings::settings->shadowTexsize;
716 activeView.shadowMapTexsize = std::min(maxSize, reqSize);
717 for (uint face = 0; face < 6; face++) {
718 glTexImage2D(dirs[face], 0, GL_DEPTH_COMPONENT, activeView.shadowMapTexsize,
719 activeView.shadowMapTexsize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
720 }
721 }
722
723 GLdouble norv[6][3] = {{1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1}};
724 GLdouble upv[6][3] = {{0, -1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1}, {0, -1, 0}, {0, -1, 0}};
725 /* order doesn't matter */
726 GLenum dirs[6] = {GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
727 GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
728 GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z};
729
730 perspectiveMatrix(90., 1, 0.1, 1000., activeView.projection);
731 glEnable(GL_CULL_FACE);
732 glEnable(GL_DEPTH_TEST);
733 glDepthFunc(GL_LEQUAL);
734 glViewport(0, 0, activeView.shadowMapTexsize, activeView.shadowMapTexsize);
735
736 GLuint cubeFBOs[6];
737 glGenFramebuffers(6, cubeFBOs);
738 for (int i = 0; i < 6; i++) {
739 /* issue: only fbo 1 is set? */
740 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, cubeFBOs[i]);
741 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, dirs[i],
742 activeView.shadowMapTexture, 0);
743 GLenum result = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
744 if (GL_FRAMEBUFFER_COMPLETE != result) {
745 warning("Framebuffer is not complete. #%d w/err %x", i, result);
746 }
747 }
748
749 glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
750 // set Persp to square + angle
751 for (int loop = 0; loop < 6; ++loop) {
752 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, cubeFBOs[loop]);
753 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
754 lookAtMatrix(activeView.light_position[0], activeView.light_position[1],
755 activeView.light_position[2], activeView.light_position[0] + norv[loop][0],
756 activeView.light_position[1] + norv[loop][1],
757 activeView.light_position[2] + norv[loop][2], upv[loop][0], upv[loop][1],
758 upv[loop][2], activeView.modelview);
759 markViewChanged();
760 // Render (todo: 50% alpha clip)
761 if (mp) mp->draw(0, focus, gm ? gm->gameTime : 0.);
762 if (gm) gm->draw();
763 }
764
765 /* back to default (to screen) frame buffer */
766 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
767 glDeleteFramebuffers(6, cubeFBOs);
768
769 activeView.calculating_shadows = false;
770 assign(origMV, activeView.modelview);
771 assign(origProj, activeView.projection);
772 markViewChanged();
773 }
774
renderShadowCascade(const Coord3d & focus,Map * mp,Game * gm)775 void renderShadowCascade(const Coord3d &focus, Map *mp, Game *gm) {
776 Matrix4d origMV, origProj;
777 assign(activeView.modelview, origMV);
778 assign(activeView.projection, origProj);
779
780 const int N = 3;
781
782 GLfloat proj_half_angle = 40 / 2 * M_PI / 180;
783 GLfloat aspect = (GLdouble)screenWidth / (GLdouble)std::max(screenHeight, 1);
784
785 /* shadow step scale by factor of 4 */
786 GLfloat dts[N + 1] = {0.f, 12.5f, 50.f, 200.f};
787 /* Construct ortho projection matrix clipped near at 0, far at 1000. */
788 Matrix4d ortho = {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, -2. / 100., -1}, {0, 0, 0, 1}};
789 assign(ortho, activeView.projection);
790
791 /* Construct light model matrices */
792 Matrix4d mvmt;
793 for (int i = 0; i < 4; i++)
794 for (int j = 0; j < 4; j++) mvmt[i][j] = origMV[j][i];
795 /* extract rotation operation and camera center */
796 Matrix3d mvm3t;
797 for (int k = 0; k < 3; k++)
798 for (int j = 0; j < 3; j++) mvm3t[j][k] = mvmt[k][j];
799 Coord3d cc(mvmt[0][3], mvmt[1][3], mvmt[2][3]);
800 Coord3d camera = -useMatrix(mvm3t, cc);
801
802 Matrix4d light_align;
803 lookAtMatrix(0, 0, 0, activeView.sun_direction[0], activeView.sun_direction[1],
804 activeView.sun_direction[2], 0, 0, 1, light_align);
805 Matrix3d light_align3;
806 for (int k = 0; k < 3; k++)
807 for (int j = 0; j < 3; j++) light_align3[j][k] = light_align[k][j];
808
809 for (int i = 0; i < N; i++) {
810 double bounds[3][2] = {{1e99, -1e99}, {1e99, -1e99}, {1e99, -1e99}};
811
812 /* Estimate orthogonal projection matrix onto frustum */
813 for (int j = 0; j < 2; j++) {
814 double eyepts[3] = {std::tan(proj_half_angle) * dts[i + j],
815 aspect * std::tan(proj_half_angle) * dts[i + j], dts[i + j]};
816 for (int k = 0; k < 4; k++) {
817 eyepts[k % 2] *= -1;
818 /* convert to world space */
819 Coord3d resultA = useMatrix(mvm3t, Coord3d(eyepts)) + camera;
820
821 /* reorient along light space. NOTE: this breaks total offset, but we don't care about
822 * it...*/
823 Coord3d result = useMatrix(light_align3, resultA);
824
825 for (int m = 0; m < 3; m++) {
826 bounds[m][0] = std::min(bounds[m][0], result[m]);
827 bounds[m][1] = std::max(bounds[m][1], result[m]);
828 }
829 }
830 }
831 double dx = bounds[0][1] - bounds[0][0], dy = bounds[1][1] - bounds[1][0]; //,
832 double rx = std::max(dx, dy);
833 double ry = std::max(dx, dy);
834 Coord3d midpoint(0, 0, -0.5 * (dts[i] + dts[i + 1]));
835 Coord3d mmpt = useMatrix(mvm3t, midpoint) + camera;
836
837 double s = 50; /* rel. the 100 of range */
838 Coord3d sundir(activeView.sun_direction[0], activeView.sun_direction[1],
839 activeView.sun_direction[2]);
840 Coord3d light_camera = mmpt - s * sundir;
841 lookAtMatrix(light_camera[0], light_camera[1], light_camera[2], mmpt[0], mmpt[1], mmpt[2],
842 0, 0, 1, activeView.cascade_model[i]);
843
844 /* like glOrtho; -L=R=rx, -B=T=ry, N=0,F=1000 */
845 const double F = 1000.;
846 Matrix4d orthoMtx = {
847 {1 / rx, 0, 0, 0}, {0, 1 / ry, 0, 0}, {0, 0, -2. / F, 0}, {0, 0, 0, 1}};
848 assign(orthoMtx, activeView.cascade_proj[i]);
849 }
850
851 /* Render cascade map using the given matrices */
852 static int ltexsize = 0;
853 if (activeView.cascadeTexsize <= 1 || ltexsize != Settings::settings->shadowTexsize) {
854 ltexsize = Settings::settings->shadowTexsize;
855 /* order doesn't matter */
856 GLint maxSize;
857 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
858 GLint reqSize = 1 << Settings::settings->shadowTexsize;
859 activeView.cascadeTexsize = std::min(maxSize, reqSize);
860 }
861 for (int i = 0; i < N; i++) {
862 glBindTexture(GL_TEXTURE_2D, activeView.cascadeTexture[i]);
863 glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, activeView.cascadeTexsize,
864 activeView.cascadeTexsize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
865 }
866
867 glEnable(GL_CULL_FACE);
868 glEnable(GL_DEPTH_TEST);
869 glDepthFunc(GL_LEQUAL);
870 glViewport(0, 0, activeView.cascadeTexsize, activeView.cascadeTexsize);
871 activeView.calculating_shadows = true;
872
873 GLuint cascadeFBOs[N];
874 glGenFramebuffers(N, cascadeFBOs);
875 for (int i = 0; i < N; i++) {
876 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, cascadeFBOs[i]);
877 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
878 activeView.cascadeTexture[i], 0);
879 GLenum result = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
880 if (GL_FRAMEBUFFER_COMPLETE != result) {
881 warning("Framebuffer is not complete. #%d w/err %x", i, result);
882 }
883 }
884
885 glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
886 // set Persp to square + angle
887 for (int loop = 0; loop < N; ++loop) {
888 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, cascadeFBOs[loop]);
889 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
890 assign(activeView.cascade_model[loop], activeView.modelview);
891 assign(activeView.cascade_proj[loop], activeView.projection);
892 markViewChanged();
893 if (mp) mp->draw(0, focus, gm ? gm->gameTime : 0.);
894 if (gm) gm->draw();
895 }
896
897 /* back to default (to screen) frame buffer */
898 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
899 glDeleteFramebuffers(N, cascadeFBOs);
900
901 assign(origMV, activeView.modelview);
902 assign(origProj, activeView.projection);
903 activeView.calculating_shadows = false;
904 markViewChanged();
905 }
906
907 /* generates a snapshot of the screen */
createSnapshot()908 int createSnapshot() {
909 static int snap_number = 0;
910 char name[1024];
911 int again = 9999;
912
913 /* find the name for the image */
914 do {
915 snprintf(name, 1023, "./snapshot_%04d.png", snap_number++);
916 FILE *f;
917 if ((f = fopen(name, "r")) == NULL) {
918 break;
919 } else {
920 fclose(f);
921 again--;
922 }
923 } while (again > 0);
924
925 /* Check against symlinks */
926 if (pathIsLink(name)) {
927 warning("file %s is a symlink.", name);
928 return 0;
929 }
930
931 /* allocate buffer */
932 unsigned char *buffer = new unsigned char[screenWidth * screenHeight * 4];
933 unsigned char *linebuffer = new unsigned char[screenWidth * 4];
934 Uint32 mask[4];
935 #if SDL_BYTEORDER == SDL_LIL_ENDIAN /* OpenGL RGBA masks */
936 mask[0] = 0x000000FF;
937 mask[1] = 0x0000FF00;
938 mask[2] = 0x00FF0000;
939 mask[3] = 0xFF000000;
940 #else
941 mask[0] = 0xFF000000;
942 mask[1] = 0x00FF0000;
943 mask[2] = 0x0000FF00;
944 mask[3] = 0x000000FF;
945 #endif
946
947 /* get the screen */
948 glReadPixels(0, 0, screenWidth, screenHeight, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid *)buffer);
949 /* Flip pixels vertically */
950 for (int i = 0; i < screenHeight / 2; i++) {
951 memcpy(linebuffer, &buffer[i * screenWidth * 4], screenWidth * 4);
952 memcpy(&buffer[i * screenWidth * 4], &buffer[(screenHeight - 1 - i) * screenWidth * 4],
953 screenWidth * 4);
954 memcpy(&buffer[(screenHeight - 1 - i) * screenWidth * 4], linebuffer, screenWidth * 4);
955 }
956 /* Construct surface and save */
957 SDL_Surface *surf =
958 SDL_CreateRGBSurfaceFrom(buffer, screenWidth, screenHeight, 32, screenWidth * 4, mask[0],
959 mask[1], mask[2], mask[3]);
960 if (IMG_SavePNG(surf, name)) { warning("Failed to save screenshot to %s", name); }
961 SDL_FreeSurface(surf);
962 /* Cleanup */
963 delete[] buffer;
964 delete[] linebuffer;
965 return 1;
966 }
967
968 /* Displays a centered semi-transparent sign with two text rows */
message(char * A,char * B)969 void message(char *A, char *B) {
970 int size = 16;
971 int w1, w2, h1, h2, w;
972 w1 = getTextWidth(A, size);
973 w2 = getTextWidth(B, size);
974 h1 = 32;
975 h2 = 32;
976
977 w = std::max(w1, w2) + 20;
978
979 int x1 = screenWidth / 2 - w / 2, x2 = screenWidth / 2 + w / 2;
980 int y1 = screenHeight / 2 - h1 - 5, y2 = screenHeight / 2 + h2 + 5;
981 Enter2DMode();
982 draw2DRectangle(x1, y1, x2 - x1, y2 - y1, 0., 0., 1., 1., 0.2, 0.5, 0.2, 0.5);
983
984 drawCenterSimpleText(A, screenWidth / 2, screenHeight / 2 - size, size, 0.5, 1.0, 0.2, 1.0);
985 drawCenterSimpleText(B, screenWidth / 2, screenHeight / 2 + 14, size, 0.5, 1.0, 0.2, 1.0);
986
987 Leave2DMode();
988 }
989
multiMessage(int nlines,const char * left[],const char * right[])990 void multiMessage(int nlines, const char *left[], const char *right[]) {
991 int total_height, width, h_now;
992 int size = 16;
993
994 total_height = 0;
995 for (int i = 0; i < nlines; i++) { total_height += size * 2; }
996 width = 600;
997
998 int x1 = screenWidth / 2 - width / 2 - 5, x2 = screenWidth / 2 + width / 2 + 5;
999 int y1 = screenHeight / 2 - total_height / 2 - 5,
1000 y2 = screenHeight / 2 + total_height / 2 + 30;
1001
1002 Enter2DMode();
1003 draw2DRectangle(x1, y1, x2 - x1, y2 - y1, 0., 0., 1., 1., 0.2, 0.5, 0.2, 0.5);
1004
1005 h_now = -size;
1006 for (int i = 0; i < nlines; i++) {
1007 h_now += 2 * size;
1008 if (left[i]) {
1009 drawSimpleText(left[i], screenWidth / 2 - width / 2,
1010 screenHeight / 2 - total_height / 2 + h_now, size, 0.5, 1.0, 0.2, 1.0);
1011 }
1012 if (right[i]) {
1013 drawRightSimpleText(right[i], screenWidth / 2 + width / 2,
1014 screenHeight / 2 - total_height / 2 + h_now, size, 0.5, 1.0, 0.2,
1015 1.0);
1016 }
1017 }
1018 Leave2DMode();
1019 }
1020
1021 /** Loads a texture from file and returns a reference to it.
1022 It is safe to load the same texture multiple times since the results are cached
1023 */
loadTexture(const char * name)1024 int loadTexture(const char *name) {
1025 /* Check in cache if texture already loaded */
1026 for (int i = 0; i < numTextures; i++)
1027 if (strcmp(textureNames[i], name) == 0) return i;
1028
1029 if (numTextures >= GLHELP_MAX_TEXTURES) {
1030 warning("Warning: max. number of textures reached (%d). Texture '%s' not loaded.",
1031 GLHELP_MAX_TEXTURES, name);
1032 return 0;
1033 }
1034
1035 char str[256];
1036 snprintf(str, sizeof(str), "%s/images/%s", effectiveShareDir, name);
1037 SDL_Surface *surface = IMG_Load(str);
1038 if (!surface) {
1039 warning("Failed to load texture %s", str);
1040 // Override texture name... (to avoid carrying on outdated tex names)
1041 textureNames[numTextures] = strdup(textureNames[0]);
1042 textures[numTextures++] =
1043 textures[0]; // just assume we managed to load this, better than nothing
1044 return -1;
1045 } else {
1046 textureNames[numTextures] = strdup(name);
1047 GLfloat texCoord[4];
1048 textures[numTextures] = LoadTexture(surface, texCoord);
1049 numTextures++;
1050 SDL_FreeSurface(surface);
1051 }
1052 return (numTextures - 1); // ok
1053 }
1054
filetobuf(const char * filename)1055 char *filetobuf(const char *filename) {
1056 FILE *fptr = fopen(filename, "rb");
1057 if (!fptr) return NULL;
1058 fseek(fptr, 0, SEEK_END);
1059 long length = ftell(fptr);
1060 char *buf = (char *)malloc(length + 1);
1061 fseek(fptr, 0, SEEK_SET);
1062 fread(buf, length, 1, fptr);
1063 fclose(fptr);
1064 buf[length] = 0;
1065 return buf;
1066 }
1067
loadShaderPart(const char * name,GLuint shader_type)1068 static GLuint loadShaderPart(const char *name, GLuint shader_type) {
1069 char path[256];
1070 const char *tdesc = NULL;
1071 if (shader_type == GL_VERTEX_SHADER) {
1072 tdesc = "Vertex";
1073 } else if (shader_type == GL_FRAGMENT_SHADER) {
1074 tdesc = "Fragment";
1075 }
1076
1077 snprintf(path, 256, "%s/shaders/%s", effectiveShareDir, name);
1078 GLchar *source = filetobuf(path);
1079 if (source == NULL) { error("%s shader %s could not be read", tdesc, path); }
1080 GLuint shader = glCreateShader(shader_type);
1081 if (shader == 0) { error("Failed to even create shader object"); }
1082 glShaderSource(shader, 1, (const GLchar **)&source, 0);
1083 free(source);
1084
1085 glCompileShader(shader);
1086 int isCompiled = 0;
1087 glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
1088 if (isCompiled == 0) {
1089 int maxLength = 0;
1090 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
1091 char *vertexInfoLog = (char *)malloc(maxLength);
1092 glGetShaderInfoLog(shader, maxLength, &maxLength, vertexInfoLog);
1093 warning("%s shader %s error (len %d): %s", tdesc, name, maxLength, vertexInfoLog);
1094 glDeleteShader(shader);
1095 free(vertexInfoLog);
1096 return 0;
1097 }
1098 return shader;
1099 }
1100
linkShader(GLuint vertexshader,GLuint fragmentshader,const char * descr)1101 static GLuint linkShader(GLuint vertexshader, GLuint fragmentshader, const char *descr) {
1102 GLuint shaderprogram = glCreateProgram();
1103 glAttachShader(shaderprogram, vertexshader);
1104 glAttachShader(shaderprogram, fragmentshader);
1105 /* Shaders use attribs 1..k for some k */
1106 glBindAttribLocation(shaderprogram, 0, "in_Position");
1107 glBindAttribLocation(shaderprogram, 1, "in_Color");
1108 glBindAttribLocation(shaderprogram, 2, "in_Texcoord");
1109 glBindAttribLocation(shaderprogram, 3, "in_Normal");
1110 glBindAttribLocation(shaderprogram, 4, "in_Velocity");
1111 glLinkProgram(shaderprogram);
1112 int is_linked;
1113 glGetProgramiv(shaderprogram, GL_LINK_STATUS, (int *)&is_linked);
1114 glDeleteShader(vertexshader);
1115 glDeleteShader(fragmentshader);
1116 if (is_linked == 0) {
1117 int maxLength = 0;
1118 glGetProgramiv(shaderprogram, GL_INFO_LOG_LENGTH, &maxLength);
1119 char *shaderProgramInfoLog = (char *)malloc(maxLength);
1120 glGetProgramInfoLog(shaderprogram, maxLength, &maxLength, shaderProgramInfoLog);
1121 warning("Program (%s) link error (len %d): %s", descr, maxLength, shaderProgramInfoLog);
1122 free(shaderProgramInfoLog);
1123 return 0;
1124 }
1125 return shaderprogram;
1126 }
1127
glHelpInit()1128 void glHelpInit() {
1129 warnForGLerrors("preGLinit");
1130
1131 TTF_Init();
1132 char str[256];
1133 snprintf(str, sizeof(str), "%s/fonts/%s", effectiveShareDir, "menuFont.ttf");
1134 ingameFont = TTF_OpenFont(str, 30);
1135 if (!ingameFont) { error("failed to load font %s", str); }
1136
1137 /* Note: all textures must be powers of 2 since we ignore texcoords */
1138 loadTexture("ice.png");
1139 loadTexture("acid.png");
1140 loadTexture("sand.png");
1141 textureTrack = textures[loadTexture("track.png")];
1142 loadTexture("texture.png");
1143 loadTexture("texture2.png");
1144 loadTexture("texture3.png");
1145 loadTexture("texture4.png");
1146 textureWings = textures[loadTexture("wings.png")];
1147 textureMousePointer = textures[loadTexture("mousePointer.png")];
1148 textureGlitter = textures[loadTexture("glitter.png")];
1149 textureDizzy = textures[loadTexture("dizzy.png")];
1150 textureBlank = textures[loadTexture("blank.png")];
1151
1152 sparkle2D = new Sparkle2D();
1153
1154 // Errors handled in loadShaderPart and linkShader
1155 GLuint vBasic = loadShaderPart("basic.vert", GL_VERTEX_SHADER);
1156 GLuint vLine = loadShaderPart("line.vert", GL_VERTEX_SHADER);
1157 GLuint vWater = loadShaderPart("water.vert", GL_VERTEX_SHADER);
1158 GLuint vUI = loadShaderPart("ui.vert", GL_VERTEX_SHADER);
1159 GLuint vObject = loadShaderPart("object.vert", GL_VERTEX_SHADER);
1160 GLuint vReflection = loadShaderPart("reflection.vert", GL_VERTEX_SHADER);
1161
1162 GLuint fBasicDay = loadShaderPart("basic_day.frag", GL_FRAGMENT_SHADER);
1163 GLuint fBasicNight = loadShaderPart("basic_night.frag", GL_FRAGMENT_SHADER);
1164 GLuint fBasicShadow = loadShaderPart("basic_shadow.frag", GL_FRAGMENT_SHADER);
1165 GLuint fLine = loadShaderPart("line.frag", GL_FRAGMENT_SHADER);
1166 GLuint fWaterDay = loadShaderPart("water_day.frag", GL_FRAGMENT_SHADER);
1167 GLuint fWaterNight = loadShaderPart("water_night.frag", GL_FRAGMENT_SHADER);
1168 GLuint fUI = loadShaderPart("ui.frag", GL_FRAGMENT_SHADER);
1169 GLuint fObjectDay = loadShaderPart("object_day.frag", GL_FRAGMENT_SHADER);
1170 GLuint fObjectNight = loadShaderPart("object_night.frag", GL_FRAGMENT_SHADER);
1171 GLuint fObjectShadow = loadShaderPart("object_shadow.frag", GL_FRAGMENT_SHADER);
1172 GLuint fReflection = loadShaderPart("reflection.frag", GL_FRAGMENT_SHADER);
1173
1174 shaderTileDay = linkShader(vBasic, fBasicDay, "BasicDay");
1175 shaderTileNight = linkShader(vBasic, fBasicNight, "BasicNight");
1176 shaderTileShadow = linkShader(vBasic, fBasicShadow, "BasicShadow");
1177 shaderLine = linkShader(vLine, fLine, "Line");
1178 shaderWaterDay = linkShader(vWater, fWaterDay, "WaterDay");
1179 shaderWaterNight = linkShader(vWater, fWaterNight, "WaterNight");
1180 shaderUI = linkShader(vUI, fUI, "UI");
1181 shaderObjectDay = linkShader(vObject, fObjectDay, "ObjectDay");
1182 shaderObjectNight = linkShader(vObject, fObjectNight, "ObjectNight");
1183 shaderObjectShadow = linkShader(vObject, fObjectShadow, "ObjectShadow");
1184 shaderReflection = linkShader(vReflection, fReflection, "Reflection");
1185
1186 glDeleteShader(vBasic);
1187 glDeleteShader(vLine);
1188 glDeleteShader(vWater);
1189 glDeleteShader(vUI);
1190 glDeleteShader(vObject);
1191 glDeleteShader(vReflection);
1192
1193 glDeleteShader(fBasicDay);
1194 glDeleteShader(fBasicNight);
1195 glDeleteShader(fBasicShadow);
1196 glDeleteShader(fLine);
1197 glDeleteShader(fWaterDay);
1198 glDeleteShader(fWaterNight);
1199 glDeleteShader(fUI);
1200 glDeleteShader(fObjectDay);
1201 glDeleteShader(fObjectNight);
1202 glDeleteShader(fObjectShadow);
1203 glDeleteShader(fReflection);
1204
1205 // Load uniform locations
1206 uniformLocations.ui_screen_width = glGetUniformLocation(shaderUI, "screen_width");
1207 uniformLocations.ui_screen_height = glGetUniformLocation(shaderUI, "screen_height");
1208 uniformLocations.ui_tex = glGetUniformLocation(shaderUI, "tex");
1209
1210 // Wipe ball cache
1211 for (int i = 0; i < MAX_BALL_DETAIL; i++) {
1212 sphere_points[i] = NULL;
1213 sphere_texcos[i] = NULL;
1214 sphere_idxs[i] = NULL;
1215 }
1216
1217 // Setup view state
1218 activeView.fog_enabled = 0;
1219 activeView.fog_color[0] = 1.f;
1220 activeView.fog_color[1] = 1.f;
1221 activeView.fog_color[2] = 1.f;
1222 activeView.fog_start = 10.f;
1223 activeView.fog_end = 20.f;
1224
1225 activeView.light_position[0] = 0.;
1226 activeView.light_position[1] = 0.;
1227 activeView.light_position[2] = 10.;
1228 activeView.light_ambient[0] = 0.2f;
1229 activeView.light_ambient[1] = 0.2f;
1230 activeView.light_ambient[2] = 0.2f;
1231 activeView.light_diffuse[0] = 1.f;
1232 activeView.light_diffuse[1] = 1.f;
1233 activeView.light_diffuse[2] = 1.f;
1234 activeView.light_specular[0] = 0.5f;
1235 activeView.light_specular[1] = 0.5f;
1236 activeView.light_specular[2] = 0.5f;
1237 activeView.sun_direction[0] = 6 / 11.;
1238 activeView.sun_direction[1] = 34 / 121.;
1239 activeView.sun_direction[2] = -93 / 121.;
1240
1241 activeView.calculating_shadows = false;
1242 activeView.show_flag_state = false;
1243
1244 glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
1245 glGenTextures(1, &activeView.shadowMapTexture);
1246 glBindTexture(GL_TEXTURE_CUBE_MAP, activeView.shadowMapTexture);
1247 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 0);
1248 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, 0);
1249 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1250 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1251 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1252 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1253 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
1254
1255 /* We don't use an array for the cascade texture because it reqs.
1256 * more shaders/etc */
1257 activeView.cascadeTexture[0] = 0;
1258 activeView.cascadeTexture[1] = 0;
1259 activeView.cascadeTexture[2] = 0;
1260
1261 glGenTextures(3, activeView.cascadeTexture);
1262 for (int i = 0; i < 3; i++) {
1263 glBindTexture(GL_TEXTURE_2D, activeView.cascadeTexture[i]);
1264
1265 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
1266 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
1267 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1268 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1269 }
1270
1271 activeView.day_mode = true;
1272
1273 renderDummyShadowCascade();
1274 renderDummyShadowMap();
1275 warnForGLerrors("postGLinit");
1276 }
glHelpCleanup()1277 void glHelpCleanup() {
1278 if (shaderTileDay) glDeleteProgram(shaderTileDay);
1279 if (shaderTileNight) glDeleteProgram(shaderTileNight);
1280 if (shaderTileShadow) glDeleteProgram(shaderTileShadow);
1281 if (shaderLine) glDeleteProgram(shaderLine);
1282 if (shaderWaterDay) glDeleteProgram(shaderWaterDay);
1283 if (shaderWaterNight) glDeleteProgram(shaderWaterNight);
1284 if (shaderUI) glDeleteProgram(shaderUI);
1285 if (shaderObjectDay) glDeleteProgram(shaderObjectDay);
1286 if (shaderObjectNight) glDeleteProgram(shaderObjectNight);
1287 if (shaderObjectShadow) glDeleteProgram(shaderObjectShadow);
1288 if (shaderReflection) glDeleteProgram(shaderReflection);
1289 shaderLine = 0;
1290 shaderTileDay = 0;
1291 shaderTileNight = 0;
1292 shaderTileShadow = 0;
1293 shaderWaterDay = 0;
1294 shaderWaterNight = 0;
1295 shaderUI = 0;
1296 shaderObjectDay = 0;
1297 shaderObjectNight = 0;
1298 shaderObjectShadow = 0;
1299
1300 if (sparkle2D) delete sparkle2D;
1301
1302 for (int i = 0; i < MAX_BALL_DETAIL; i++) {
1303 if (sphere_points[i]) delete[] sphere_points[i];
1304 if (sphere_texcos[i]) delete[] sphere_texcos[i];
1305 if (sphere_idxs[i]) delete[] sphere_idxs[i];
1306 }
1307
1308 /* Invalidate all strings so they get cleaned up */
1309 update2DStringCache(true);
1310
1311 for (std::map<int, TTF_Font *>::iterator i = menuFontLookup.begin();
1312 i != menuFontLookup.end(); ++i) {
1313 TTF_CloseFont(i->second);
1314 }
1315 menuFontLookup.clear();
1316 TTF_CloseFont(ingameFont);
1317 ingameFont = 0;
1318
1319 for (int i = 0; i < numTextures; i++) { glDeleteTextures(1, &textures[i]); }
1320 }
1321
warnForGLerrors(const char * where_am_i)1322 void warnForGLerrors(const char *where_am_i) {
1323 GLenum err;
1324 while ((err = glGetError()) != GL_NO_ERROR) {
1325 const char *type = NULL;
1326 switch (err) {
1327 default:
1328 break;
1329 case GL_INVALID_ENUM:
1330 type = "Invalid enum";
1331 break;
1332 case GL_INVALID_VALUE:
1333 type = "Invalid value";
1334 break;
1335 case GL_INVALID_OPERATION:
1336 type = "Invalid operation";
1337 break;
1338 }
1339
1340 warning("GL error %x (%s) at location: %s", err, type, where_am_i);
1341 }
1342 }
1343
1344 /* Calculates and displays current framerate */
displayFrameRate()1345 void displayFrameRate() {
1346 /* Frame rate is given for the previous period. */
1347 double td = getTimeDifference(lastDisplayStartTime, displayStartTime) / timeDilationFactor;
1348 if (td > 1.0) {
1349 fps = 1.0;
1350 } else if (td <= 1e-4) {
1351 static int hasWarned = 0;
1352 if (!hasWarned) {
1353 warning("Warning: too fast framerate (%f)", td);
1354 hasWarned = 1;
1355 }
1356 } else {
1357 fps = fps * 0.95 + 0.05 / td;
1358 }
1359
1360 if (Settings::settings->showFPS > 0) {
1361 char slow[256];
1362 char fast[256];
1363 if (Settings::settings->showFPS == 1) {
1364 snprintf(slow, sizeof(slow), "%s ", _("Framerate:"));
1365 if (fps > 0) {
1366 snprintf(fast, sizeof(fast), "%.1f", fps);
1367 } else {
1368 snprintf(fast, sizeof(fast), _("unknown"));
1369 }
1370 } else {
1371 snprintf(slow, sizeof(slow), "%s ", _("ms/Frame:"));
1372 snprintf(fast, sizeof(fast), "%.1f", 1e3 * td);
1373 }
1374 TTF_Font *active = menuFontForSize(10);
1375 int w1 = draw2DString(active, slow, 15, screenHeight - 15, menuColor[0], menuColor[1],
1376 menuColor[2], menuColor[3], true, 0, 0);
1377 draw2DString(active, fast, 15 + w1, screenHeight - 15, menuColor[0], menuColor[1],
1378 menuColor[2], menuColor[3], true, 0, 0);
1379 }
1380 }
1381
1382 /*********************/
1383 /* Matrix operations */
1384 /*********************/
1385
debugMatrix(Matrix4d m)1386 void debugMatrix(Matrix4d m) {
1387 warning("%8.5f \t%8.5f \t%8.5f \t%8.5f", m[0][0], m[0][1], m[0][2], m[0][3]);
1388 warning("%8.5f \t%8.5f \t%8.5f \t%8.5f", m[1][0], m[1][1], m[1][2], m[1][3]);
1389 warning("%8.5f \t%8.5f \t%8.5f \t%8.5f", m[2][0], m[2][1], m[2][2], m[2][3]);
1390 warning("%8.5f \t%8.5f \t%8.5f \t%8.5f", m[3][0], m[3][1], m[3][2], m[3][3]);
1391 }
1392
1393 /* C <- A * B */
matrixMult(const Matrix4d A,const Matrix4d B,Matrix4d C)1394 void matrixMult(const Matrix4d A, const Matrix4d B, Matrix4d C) {
1395 for (int i = 0; i < 4; i++)
1396 for (int j = 0; j < 4; j++) {
1397 C[i][j] = 0.0;
1398 for (int k = 0; k < 4; k++) C[i][j] += A[i][k] * B[k][j];
1399 }
1400 }
1401
1402 /* C <- A(B) */
useMatrix(Matrix4d A,const Coord3d & B)1403 Coord3d useMatrix(Matrix4d A, const Coord3d &B) {
1404 Coord3d C;
1405 for (int i = 0; i < 3; i++) {
1406 C[i] = A[i][3];
1407 for (int k = 0; k < 3; k++) C[i] += A[i][k] * B[k];
1408 }
1409 double h = A[3][3];
1410 for (int k = 0; k < 3; k++) h += A[3][k];
1411 for (int k = 0; k < 3; k++) C[k] /= h;
1412 return C;
1413 }
1414
1415 /* C <- A(B) */
useMatrix(Matrix3d A,const Coord3d & B)1416 Coord3d useMatrix(Matrix3d A, const Coord3d &B) {
1417 Coord3d C;
1418 for (int i = 0; i < 3; i++) {
1419 for (int k = 0; k < 3; k++) C[i] += A[i][k] * B[k];
1420 }
1421 return C;
1422 }
1423
1424 /* C <- A */
assign(const Matrix4d A,Matrix4d C)1425 void assign(const Matrix4d A, Matrix4d C) {
1426 for (int i = 0; i < 4; i++)
1427 for (int j = 0; j < 4; j++) C[i][j] = A[i][j];
1428 }
1429
1430 /* C <- A */
identityMatrix(Matrix4d m)1431 void identityMatrix(Matrix4d m) {
1432 for (int i = 0; i < 4; i++)
1433 for (int j = 0; j < 4; j++) m[i][j] = i == j ? 1.0 : 0.0;
1434 }
1435
rotateX(double v,Matrix4d m)1436 void rotateX(double v, Matrix4d m) {
1437 double cv = std::cos(v), sv = std::sin(v);
1438 for (int i = 0; i < 4; i++) {
1439 double r1 = m[i][1], r2 = m[i][2];
1440 m[i][1] = cv * r1 - sv * r2;
1441 m[i][2] = sv * r1 + cv * r2;
1442 }
1443 }
rotateY(double v,Matrix4d m)1444 void rotateY(double v, Matrix4d m) {
1445 double cv = std::cos(v), sv = std::sin(v);
1446 for (int i = 0; i < 4; i++) {
1447 double r0 = m[i][0], r2 = m[i][2];
1448 m[i][0] = cv * r0 - sv * r2;
1449 m[i][2] = sv * r0 + cv * r2;
1450 }
1451 }
rotateZ(double v,Matrix4d m)1452 void rotateZ(double v, Matrix4d m) {
1453 double cv = std::cos(v), sv = std::sin(v);
1454 for (int i = 0; i < 4; i++) {
1455 double r0 = m[i][0], r1 = m[i][1];
1456 m[i][0] = cv * r0 - sv * r1;
1457 m[i][1] = sv * r0 + cv * r1;
1458 }
1459 }
testBboxClip(double x1,double x2,double y1,double y2,double z1,double z2,const Matrix4d mvp)1460 bool testBboxClip(double x1, double x2, double y1, double y2, double z1, double z2,
1461 const Matrix4d mvp) {
1462 // Construct axis-aligned bounding box in window space
1463 double vxl = 1e99, vyl = 1e99, vzl = 1e99;
1464 double vxh = -1e99, vyh = -1e99, vzh = -1e99;
1465 for (int i = 0; i < 8; i++) {
1466 double w[3] = {0., 0., 0.};
1467 double p[3] = {(i & 4) ? x1 : x2, (i & 2) ? y1 : y2, (i & 1) ? z1 : z2};
1468 // Apply perspective transform with MVP matrix
1469 for (int k = 0; k < 3; k++)
1470 w[k] += mvp[0][k] * p[0] + mvp[1][k] * p[1] + mvp[2][k] * p[2] + mvp[3][k];
1471 double h = mvp[0][3] * p[0] + mvp[1][3] * p[1] + mvp[2][3] * p[2] + mvp[3][3];
1472 for (int k = 0; k < 3; k++) w[k] /= h;
1473
1474 vxl = std::min(vxl, w[0]);
1475 vyl = std::min(vyl, w[1]);
1476 vzl = std::min(vzl, w[2]);
1477
1478 vxh = std::max(vxh, w[0]);
1479 vyh = std::max(vyh, w[1]);
1480 vzh = std::max(vzh, w[2]);
1481 }
1482 // Window frustum is [-1,1]x[-1,1]x[0,1]
1483 if (vxl > 1 || vxh < -1) return false;
1484 if (vyl > 1 || vyh < -1) return false;
1485 if (vzl > 1 || vzh < 0) return false;
1486 return true;
1487 }
1488
1489 static bool is_2d_mode = false;
1490
Require2DMode()1491 void Require2DMode() {
1492 if (!is_2d_mode) { warning("Function `Require2DMode` should only be called in 2D mode"); }
1493 }
1494
Enter2DMode()1495 void Enter2DMode() {
1496 if (is_2d_mode) { return; }
1497
1498 glDisable(GL_DEPTH_TEST);
1499 glDisable(GL_CULL_FACE);
1500
1501 glEnable(GL_BLEND);
1502 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1503
1504 // Load program and set up uniforms. VAO will by bound at point of use
1505 glUseProgram(shaderUI);
1506 glUniform1f(uniformLocations.ui_screen_width, (GLfloat)screenWidth);
1507 glUniform1f(uniformLocations.ui_screen_height, (GLfloat)screenHeight);
1508 glUniform1i(uniformLocations.ui_tex, 0);
1509
1510 glActiveTexture(GL_TEXTURE0);
1511 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
1512 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
1513 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1514 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1515
1516 is_2d_mode = true;
1517 }
1518
Leave2DMode()1519 void Leave2DMode() {
1520 if (!is_2d_mode) {
1521 warning("Function `Leave2DMode` should only be called in 2D mode");
1522 return;
1523 }
1524
1525 is_2d_mode = false;
1526 }
1527
powerOfTwo(int input)1528 int powerOfTwo(int input) {
1529 int value = 1;
1530
1531 while (value < input) { value <<= 1; }
1532 return value;
1533 }
1534
LoadTexture(SDL_Surface * surface,GLfloat * texcoord)1535 GLuint LoadTexture(SDL_Surface *surface, GLfloat *texcoord) {
1536 /* Use the surface width and height expanded to powers of 2 */
1537 int w = powerOfTwo(surface->w);
1538 int h = powerOfTwo(surface->h);
1539
1540 texcoord[0] = 0.0f; /* Min X */
1541 texcoord[1] = 0.0f; /* Min Y */
1542 texcoord[2] = (GLfloat)surface->w / w; /* Max X */
1543 texcoord[3] = (GLfloat)surface->h / h; /* Max Y */
1544
1545 /* Rescale image if needed? */
1546 GLint maxSize;
1547 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
1548 double scale = 1.0;
1549 if (w > maxSize || h > maxSize) scale = std::min(maxSize / (double)w, maxSize / (double)h);
1550
1551 Uint32 mask[4];
1552 #if SDL_BYTEORDER == SDL_LIL_ENDIAN /* OpenGL RGBA masks */
1553 mask[0] = 0x000000FF;
1554 mask[1] = 0x0000FF00;
1555 mask[2] = 0x00FF0000;
1556 mask[3] = 0xFF000000;
1557 #else
1558 mask[0] = 0xFF000000;
1559 mask[1] = 0x00FF0000;
1560 mask[2] = 0x0000FF00;
1561 mask[3] = 0x000000FF;
1562 #endif
1563
1564 SDL_Surface *image = SDL_CreateRGBSurface(SDL_SWSURFACE, (int)(w * scale), (int)(h * scale),
1565 32, mask[0], mask[1], mask[2], mask[3]);
1566
1567 if (image == NULL) { return 0; }
1568
1569 /* Copy the surface into the GL texture image */
1570 SDL_Rect area;
1571 area.x = 0;
1572 area.y = 0;
1573 area.w = (int)(surface->w * scale);
1574 area.h = (int)(surface->h * scale);
1575
1576 SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE);
1577 if (scale == 1.0)
1578 // Easy, no scaling needed
1579 SDL_BlitSurface(surface, NULL, image, &area);
1580 else {
1581 SDL_Rect sarea;
1582 sarea.x = 0;
1583 sarea.y = 0;
1584 sarea.h = surface->h;
1585 sarea.w = surface->w;
1586 SDL_BlitScaled(surface, &sarea, image, &area);
1587 }
1588
1589 /* Create an OpenGL texture for the image */
1590 GLuint texture = 0;
1591 glGenTextures(1, &texture);
1592 glBindTexture(GL_TEXTURE_2D, texture);
1593 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)(w * scale), (int)(h * scale), 0, GL_RGBA,
1594 GL_UNSIGNED_BYTE, image->pixels);
1595 SDL_FreeSurface(image); /* No longer needed */
1596
1597 /* Set current sampler with these just in case */
1598 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
1599 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
1600 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1601 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1602
1603 return texture;
1604 }
1605
loadImage(const char * imagename)1606 SDL_Surface *loadImage(const char *imagename) {
1607 char path[512];
1608 snprintf(path, 511, "%s/images/%s", effectiveShareDir, imagename);
1609 path[511] = '\0';
1610
1611 SDL_Surface *img = IMG_Load(path);
1612 if (!img) error("Failed to load image '%s' because: %s", path, IMG_GetError());
1613 return img;
1614 }
1615