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