1 /**\file r_common.c
2 *\section License
3 * License: GPL
4 * Online License Link: http://www.gnu.org/licenses/gpl.html
5 *
6 *\author Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
7 *\author Copyright © 2005-2013 Daniel Swanson <danij@dengine.net>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor,
22 * Boston, MA 02110-1301 USA
23 */
24
25 /**
26 * Common routines for refresh.
27 */
28
29 #include <assert.h>
30 #include <math.h>
31 #include <string.h>
32 #include <stdio.h>
33
34 #if __JDOOM__
35 # include "jdoom.h"
36 #elif __JDOOM64__
37 # include "jdoom64.h"
38 #elif __JHERETIC__
39 # include "jheretic.h"
40 #elif __JHEXEN__
41 # include "jhexen.h"
42 #endif
43
44 #include "g_common.h"
45 #include "g_controls.h"
46 #include "hu_stuff.h"
47 #include "p_actor.h"
48 #include "player.h"
49 #include "r_common.h"
50 #include "r_special.h"
51 #include "x_hair.h"
52
53 Size2Rawf viewScale = { 1, 1 };
54 float aspectScale = 1;
55
56 static int gammaLevel;
57 #ifndef __JHEXEN__
58 char gammamsg[5][81];
59 #endif
60
R_PrecachePSprites(void)61 void R_PrecachePSprites(void)
62 {
63 int i, k;
64
65 if(IS_DEDICATED)
66 return;
67
68 for(i = 0; i < NUM_WEAPON_TYPES; ++i)
69 {
70 for(k = 0; k < NUMWEAPLEVELS; ++k)
71 {
72 int pclass = players[CONSOLEPLAYER].class_;
73
74 Models_CacheForState(weaponInfo[i][pclass].mode[k].states[WSN_UP]);
75 Models_CacheForState(weaponInfo[i][pclass].mode[k].states[WSN_DOWN]);
76 Models_CacheForState(weaponInfo[i][pclass].mode[k].states[WSN_READY]);
77 Models_CacheForState(weaponInfo[i][pclass].mode[k].states[WSN_ATTACK]);
78 Models_CacheForState(weaponInfo[i][pclass].mode[k].states[WSN_FLASH]);
79 #if __JHERETIC__ || __JHEXEN__
80 Models_CacheForState(weaponInfo[i][pclass].mode[k].states[WSN_ATTACK_HOLD]);
81 #endif
82 }
83 }
84 }
85
86 /// @return @c true= maximized view window is in effect.
maximizedViewWindow(int player)87 static dd_bool maximizedViewWindow(int player)
88 {
89 player_t* plr = players + player;
90 if(player < 0 || player >= MAXPLAYERS)
91 {
92 Con_Error("maximizedViewWindow: Invalid player #%i.", player);
93 exit(1);
94 }
95 return (!(G_GameState() == GS_MAP && cfg.common.screenBlocks <= 10 &&
96 !(P_MobjIsCamera(plr->plr->mo) && Get(DD_PLAYBACK)))); // $democam: can be set on every game tic.
97 }
98
calcStatusBarSize(Size2Raw * size,Size2Rawf const * viewScale,int maxWidth)99 static void calcStatusBarSize(Size2Raw *size, Size2Rawf const *viewScale, int maxWidth)
100 {
101 /**
102 * @todo Refactor: This information should be queried from the status bar widget. -jk
103 */
104 #if __JDOOM__ || __JHERETIC__ || __JHEXEN__
105 float factor = 1;
106
107 float const VGA_ASPECT = 1.f/1.2f;
108 float const aspectRatio = viewScale->width / viewScale->height;
109
110 if(aspectRatio < VGA_ASPECT)
111 {
112 // We're below the VGA aspect, which means the status bar will be
113 // scaled smaller.
114 factor *= aspectRatio / VGA_ASPECT;
115 }
116
117 factor *= cfg.common.statusbarScale;
118
119 size->width = ST_WIDTH * factor;
120 size->height = ceil(ST_HEIGHT * factor);
121 #else
122 size->width = size->height = 0;
123 #endif
124 }
125
R_StatusBarSize(int player,Size2Raw * statusBarSize)126 void R_StatusBarSize(int player, Size2Raw *statusBarSize)
127 {
128 Size2Raw viewSize;
129 R_ViewWindowSize(player, &viewSize);
130 calcStatusBarSize(statusBarSize, &viewScale, viewSize.width);
131 }
132
resizeViewWindow(int player,const RectRaw * newGeometry,const RectRaw * oldGeometry,dd_bool interpolate)133 static void resizeViewWindow(int player, const RectRaw* newGeometry,
134 const RectRaw* oldGeometry, dd_bool interpolate)
135 {
136 RectRaw window;
137
138 DENG_ASSERT(newGeometry);
139 DENG_ASSERT(player >= 0 && player < MAXPLAYERS);
140
141 DENG_UNUSED(oldGeometry);
142
143 // Calculate fixed 320x200 scale factors.
144 viewScale.width = (float)newGeometry->size.width / SCREENWIDTH;
145 viewScale.height = (float)newGeometry->size.height / SCREENHEIGHT;
146 aspectScale = newGeometry->size.width >= newGeometry->size.height? viewScale.width : viewScale.height;
147
148 // Determine view window geometry.
149 memcpy(&window, newGeometry, sizeof(window));
150 window.origin.x = window.origin.y = 0;
151
152 // Override @c cfg.common.screenBlocks and force a maximized window?
153 if(!maximizedViewWindow(player) && cfg.common.screenBlocks <= 10)
154 {
155 Size2Raw statusBarSize;
156 calcStatusBarSize(&statusBarSize, &viewScale, newGeometry->size.width);
157
158 if(cfg.common.screenBlocks != 10)
159 {
160 window.size.width = cfg.common.screenBlocks * SCREENWIDTH/10;
161 window.size.height = cfg.common.screenBlocks * (SCREENHEIGHT - statusBarSize.height) / 10;
162
163 window.origin.x = (SCREENWIDTH - window.size.width) / 2;
164 window.origin.y = (SCREENHEIGHT - statusBarSize.height - window.size.height) / 2;
165 }
166 else
167 {
168 window.origin.x = 0;
169 window.origin.y = 0;
170 window.size.width = SCREENWIDTH;
171 window.size.height = SCREENHEIGHT - statusBarSize.height;
172 }
173
174 // Scale from fixed to viewport coordinates.
175 window.origin.x = ROUND(window.origin.x * viewScale.width);
176 window.origin.y = ROUND(window.origin.y * viewScale.height);
177 window.size.width = ROUND(window.size.width * viewScale.width);
178 window.size.height = ROUND(window.size.height * viewScale.height);
179 }
180
181 R_SetViewWindowGeometry(player, &window, interpolate);
182 }
183
R_ResizeViewWindow(int flags)184 void R_ResizeViewWindow(int flags)
185 {
186 static dd_bool oldMaximized;
187 int i, delta, destBlocks = MINMAX_OF(3, cfg.common.setBlocks, 13);
188 dd_bool maximized;
189 RectRaw port;
190
191 if(IS_DEDICATED) return;
192
193 // Override @c cfg.common.screenBlocks and force a maximized window?
194 maximized = maximizedViewWindow(DISPLAYPLAYER);
195 if(maximized != oldMaximized)
196 {
197 oldMaximized = maximized;
198 flags |= RWF_FORCE|RWF_NO_LERP;
199 }
200
201 if(!(flags & RWF_FORCE))
202 {
203 if(cfg.common.screenBlocks == destBlocks)
204 return;
205 }
206
207 delta = MINMAX_OF(-1, destBlocks - cfg.common.screenBlocks, 1);
208 if(delta != 0)
209 {
210 if(cfg.common.screenBlocks >= 10 && destBlocks != 13)
211 {
212 // When going fullscreen, force a hud show event (to reset the timer).
213 for(i = 0; i < MAXPLAYERS; ++i)
214 ST_HUDUnHide(i, HUE_FORCE);
215 }
216
217 if((cfg.common.screenBlocks == 11 && destBlocks == 10) ||
218 (cfg.common.screenBlocks == 10 && destBlocks == 11))
219 {
220 // When going to/from statusbar span, do an instant change.
221 flags |= RWF_NO_LERP;
222 }
223
224 cfg.common.screenBlocks += delta;
225 flags |= RWF_FORCE;
226 }
227
228 // No update necessary?
229 if(!(flags & RWF_FORCE)) return;
230
231 for(i = 0; i < MAXPLAYERS; ++i)
232 {
233 if(!R_ViewPortGeometry(i, &port))
234 {
235 // Player is not local or does not have a viewport.
236 continue;
237 }
238 resizeViewWindow(i, &port, &port, (flags & RWF_NO_LERP)==0);
239 }
240 }
241
R_UpdateViewport(int hookType,int param,void * data)242 int R_UpdateViewport(int hookType, int param, void* data)
243 {
244 const ddhook_viewport_reshape_t* p = (ddhook_viewport_reshape_t*)data;
245 resizeViewWindow(param, &p->geometry, &p->oldGeometry, false);
246 return true;
247 }
248
249 #ifndef __JHEXEN__
R_GetGammaMessageStrings(void)250 void R_GetGammaMessageStrings(void)
251 {
252 int i;
253 for(i = 0; i < 5; ++i)
254 {
255 strcpy(gammamsg[i], GET_TXT(TXT_GAMMALVL0 + i));
256 }
257 }
258 #endif
259
R_CycleGammaLevel(void)260 void R_CycleGammaLevel(void)
261 {
262 char buf[50];
263
264 if(G_QuitInProgress()) return;
265
266 gammaLevel++;
267 if(gammaLevel > 4)
268 gammaLevel = 0;
269
270 #if __JDOOM__ || __JDOOM64__
271 P_SetMessageWithFlags(&players[CONSOLEPLAYER], gammamsg[gammaLevel], LMF_NO_HIDE);
272 #endif
273
274 sprintf(buf, "rend-tex-gamma %f", ((float) gammaLevel / 8.0f) * 1.5f);
275 DD_Execute(false, buf);
276 }
277
278 /**
279 * Tells the engine where the camera is located. This has to be called before
280 * the end of G_Ticker() (after thinkers have been run), so that the up-to-date
281 * sharp camera position and angles are available when the new sharp world is
282 * saved.
283 *
284 * @param player Player # to update.
285 */
R_UpdateConsoleView(int player)286 void R_UpdateConsoleView(int player)
287 {
288 coord_t viewOrigin[3];
289 player_t* plr;
290 mobj_t* mo;
291
292 if(IS_DEDICATED || player < 0 || player >= MAXPLAYERS) return;
293 plr = &players[player];
294 mo = plr->plr->mo;
295 if(!mo || !plr->plr->inGame) return; // Not present?
296
297 viewOrigin[VX] = mo->origin[VX] + plr->viewOffset[VX];
298 viewOrigin[VY] = mo->origin[VY] + plr->viewOffset[VY];
299 viewOrigin[VZ] = plr->viewZ + plr->viewOffset[VZ];
300 R_SetViewOrigin(player, viewOrigin);
301 R_SetViewAngle(player, Player_ViewYawAngle(player));
302 R_SetViewPitch(player, plr->plr->lookDir);
303 }
304
rendHUD(int player,const RectRaw * portGeometry)305 static void rendHUD(int player, const RectRaw* portGeometry)
306 {
307 if(player < 0 || player >= MAXPLAYERS) return;
308 if(G_GameState() != GS_MAP) return;
309 if(IS_CLIENT && (!Get(DD_GAME_READY) || !Get(DD_GOTFRAME))) return;
310 if(!DD_GetInteger(DD_GAME_DRAW_HUD_HINT)) return; // The engine advises not to draw any HUD displays.
311
312 ST_Drawer(player);
313 HU_DrawScoreBoard(player);
314 Hu_MapTitleDrawer(portGeometry);
315 }
316
G_DrawViewPort(int port,RectRaw const * portGeometry,RectRaw const * windowGeometry,int player,int layer)317 void G_DrawViewPort(int port, RectRaw const *portGeometry,
318 RectRaw const *windowGeometry, int player, int layer)
319 {
320 switch (G_GameState())
321 {
322 case GS_MAP: {
323 player_t* plr = players + player;
324 dd_bool isAutomapObscuring = ST_AutomapObscures2(player, windowGeometry);
325
326 if (IS_CLIENT && (!Get(DD_GAME_READY) || !Get(DD_GOTFRAME)))
327 return;
328
329 if (cfg.common.automapNeverObscure || Con_GetInteger("rend-vr-mode") == 9) // Oculus Rift mode
330 {
331 // Automap will not cover the full view.
332 isAutomapObscuring = false;
333 }
334
335 switch (layer)
336 {
337 case 0: // Primary layer (3D view).
338 if (!isAutomapObscuring)
339 {
340 G_RendPlayerView(player);
341 #if defined(__JDOOM64__)
342 G_RendSpecialFilter(player, windowGeometry);
343 #endif
344 }
345 break;
346
347 default: // HUD layer.
348 // Crosshair.
349 if (!isAutomapObscuring && !(P_MobjIsCamera(plr->plr->mo) && Get(DD_PLAYBACK))) // $democam
350 {
351 X_Drawer(player);
352 }
353
354 // Other HUD elements.
355 rendHUD(player, portGeometry);
356 break;
357 }
358 break; }
359
360 case GS_STARTUP:
361 if (layer == 0)
362 {
363 DGL_DrawRectf2Color(0, 0, portGeometry->size.width, portGeometry->size.height,
364 0, 0, 0, 1);
365 }
366 break;
367
368 default:
369 break;
370 }
371 }
372
G_ResetViewEffects()373 void G_ResetViewEffects()
374 {
375 GL_ResetViewEffects();
376 R_InitSpecialFilter();
377 }
378