2009-03-05 09:03:26 +00:00
|
|
|
/*
|
2010-10-18 14:56:30 +00:00
|
|
|
* Copyright (C) 1997-2001 Id Software, Inc.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or (at
|
|
|
|
* your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
*
|
|
|
|
* See the GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
|
|
* 02111-1307, USA.
|
|
|
|
*
|
|
|
|
* =======================================================================
|
|
|
|
*
|
|
|
|
* This is the "heart" of the id Tech 2 refresh engine. This file
|
|
|
|
* implements the main window in which Quake II is running. The window
|
|
|
|
* itself is created by the SDL backend, but here the refresh module is
|
|
|
|
* loaded, initialized and it's interaction with the operating system
|
|
|
|
* implemented. This code is also the interconnect between the input
|
|
|
|
* system (the mouse) and the keyboard system, both are here tied
|
|
|
|
* together with the refresher. The direct interaction between the
|
|
|
|
* refresher and those subsystems are the main cause for the very
|
|
|
|
* acurate and precise input controls of the id Tech 2.
|
|
|
|
*
|
2012-08-02 12:00:04 +00:00
|
|
|
* This implementation works for Windows and unixoid systems, but
|
|
|
|
* other platforms may need an own implementation!
|
|
|
|
*
|
2010-10-18 14:56:30 +00:00
|
|
|
* =======================================================================
|
|
|
|
*/
|
2009-03-05 09:03:26 +00:00
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
2012-08-01 11:58:10 +00:00
|
|
|
#include "../../client/header/client.h"
|
2015-01-28 19:55:28 +00:00
|
|
|
#include "../../client/header/keyboard.h"
|
2009-03-05 09:03:26 +00:00
|
|
|
|
2018-07-31 09:54:35 +00:00
|
|
|
// --------
|
|
|
|
|
|
|
|
// Screenshots
|
|
|
|
// -----------
|
|
|
|
|
2017-03-12 23:03:13 +00:00
|
|
|
#ifdef ZIP
|
2018-07-31 09:54:35 +00:00
|
|
|
// If we build with zip support, zlib is available and
|
|
|
|
// we can use that for better PNG compression.
|
|
|
|
|
2017-03-12 23:03:13 +00:00
|
|
|
#include <zlib.h>
|
|
|
|
|
|
|
|
static unsigned char*
|
|
|
|
compress_for_stbiw(unsigned char *data, int data_len, int *out_len, int quality)
|
|
|
|
{
|
|
|
|
uLongf bufSize = compressBound(data_len);
|
|
|
|
unsigned char* buf = malloc(bufSize);
|
2018-07-31 09:54:35 +00:00
|
|
|
|
|
|
|
if (buf == NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (compress2(buf, &bufSize, data, data_len, quality) != Z_OK)
|
2017-03-12 23:03:13 +00:00
|
|
|
{
|
|
|
|
free(buf);
|
|
|
|
return NULL;
|
|
|
|
}
|
2018-07-31 09:54:35 +00:00
|
|
|
|
2017-03-12 23:03:13 +00:00
|
|
|
*out_len = bufSize;
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
2018-07-31 09:54:35 +00:00
|
|
|
|
2017-03-12 23:03:13 +00:00
|
|
|
#define STBIW_ZLIB_COMPRESS compress_for_stbiw
|
2018-07-31 09:54:35 +00:00
|
|
|
#endif
|
2017-03-12 23:03:13 +00:00
|
|
|
|
Screenshots can be tga, bmp, png, jpg now, using stb_image_write.h
the screenshot command now supports the filetype as optional argument
(just "screenshot" will use tga like before):
"screenshot png" will save the screenshot as PNG, same with jpg, png
and tga.
For jpg, you can even specify the quality, like "screenshot jpg 90"
(the Quality is between 1 and 100, like with libjpeg).
To reduce duplicated code, I addeed Vid_WriteScreenshot() to refimport_t
and implement most of it in the client (vid.c).
The renderer still fetches the raw image data from OpenGL or whatever
and then calls re.VidWriteScreenshot() which will write it to disk in
the format requested by the user.
2017-03-12 02:28:06 +00:00
|
|
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
|
|
#include "header/stb_image_write.h"
|
|
|
|
|
2018-07-31 09:54:35 +00:00
|
|
|
/*
|
|
|
|
* Writes a screenshot. This function is called with raw image data of
|
|
|
|
* width*height pixels, each pixel has comp bytes. Must be 3 or 4, for
|
|
|
|
* RGB or RGBA. The pixels must be given row-wise, stating at the top
|
|
|
|
* left.
|
|
|
|
*/
|
|
|
|
void VID_WriteScreenshot(int width, int height, int comp, const void* data)
|
|
|
|
{
|
|
|
|
char picname[80];
|
|
|
|
char checkname[MAX_OSPATH];
|
|
|
|
int i, success = 0;
|
|
|
|
static const char* supportedFormats[] = { "tga", "bmp", "png", "jpg" };
|
|
|
|
static const int numFormats = sizeof(supportedFormats)/sizeof(supportedFormats[0]);
|
|
|
|
int format = 0; // 0=tga, 1=bmp, 2=png, 3=jpg
|
|
|
|
int quality = 85;
|
|
|
|
int argc = Cmd_Argc();
|
|
|
|
const char* gameDir = FS_Gamedir();
|
|
|
|
|
|
|
|
// FS_InitFilesystem() made sure the screenshots dir exists./
|
|
|
|
|
|
|
|
if (argc > 1)
|
|
|
|
{
|
|
|
|
const char* maybeFormat = Cmd_Argv(1);
|
|
|
|
|
|
|
|
for (i = 0; i < numFormats; ++i)
|
|
|
|
{
|
|
|
|
if (Q_stricmp(maybeFormat, supportedFormats[i]) == 0)
|
|
|
|
{
|
|
|
|
format = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == numFormats)
|
|
|
|
{
|
|
|
|
Com_Printf("the (optional) second argument to 'screenshot' is the format, one of \"tga\", \"bmp\", \"png\", \"jpg\"\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argc > 2)
|
|
|
|
{
|
|
|
|
const char* q = Cmd_Argv(2);
|
|
|
|
int qualityStrLen = strlen(q);
|
|
|
|
|
|
|
|
for (i = 0; i < qualityStrLen; ++i)
|
|
|
|
{
|
|
|
|
if (q[i] < '0' || q[i] > '9')
|
|
|
|
{
|
|
|
|
Com_Printf("The (optional!) third argument to 'screenshot' is jpg quality, a number between 1 and 100\n");
|
|
|
|
Com_Printf(" or png compression level, between 0 and 9!\n");
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
quality = atoi(q);
|
|
|
|
|
|
|
|
if (format == 2) // png
|
|
|
|
{
|
|
|
|
if (quality < 0)
|
|
|
|
{
|
|
|
|
quality = 0;
|
|
|
|
}
|
|
|
|
else if (quality > 9)
|
|
|
|
{
|
|
|
|
quality = 9;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(format == 3) // jpg
|
|
|
|
{
|
|
|
|
if (quality < 1)
|
|
|
|
{
|
|
|
|
quality = 1;
|
|
|
|
}
|
|
|
|
else if (quality > 100)
|
|
|
|
{
|
|
|
|
quality = 100;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* find a file name to save it to */
|
|
|
|
for (i = 0; i <= 9999; i++)
|
|
|
|
{
|
|
|
|
FILE *f;
|
|
|
|
Com_sprintf(checkname, sizeof(checkname), "%s/scrnshot/q2_%04d.%s", gameDir, i, supportedFormats[format]);
|
|
|
|
f = Q_fopen(checkname, "rb");
|
|
|
|
|
|
|
|
if (!f)
|
|
|
|
{
|
|
|
|
Com_sprintf(picname, sizeof(picname), "q2_%04d.%s", i, supportedFormats[format]);
|
|
|
|
break; /* file doesn't exist */
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == 10000)
|
|
|
|
{
|
|
|
|
Com_Printf("SCR_ScreenShot_f: Couldn't create a file\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (format) // 0=tga, 1=bmp, 2=png, 3=jpg
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
success = stbi_write_tga(checkname, width, height, comp, data);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
success = stbi_write_bmp(checkname, width, height, comp, data);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
stbi_write_png_compression_level = (quality < 10) ? quality : 7;
|
|
|
|
success = stbi_write_png(checkname, width, height, comp, data, 0);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
success = stbi_write_jpg(checkname, width, height, comp, data, quality);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(success)
|
|
|
|
{
|
|
|
|
Com_Printf("Wrote %s\n", picname);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Com_Printf("SCR_ScreenShot_f: Couldn't write %s\n", picname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------
|
|
|
|
|
|
|
|
// Video mode array
|
|
|
|
// ----------------
|
2016-10-01 22:40:52 +00:00
|
|
|
|
2013-05-01 15:15:35 +00:00
|
|
|
typedef struct vidmode_s
|
|
|
|
{
|
|
|
|
const char *description;
|
|
|
|
int width, height;
|
|
|
|
int mode;
|
|
|
|
} vidmode_t;
|
|
|
|
|
2018-07-31 09:54:35 +00:00
|
|
|
// This must be the same as in videomenu.c!
|
2013-05-01 15:15:35 +00:00
|
|
|
vidmode_t vid_modes[] = {
|
2017-02-25 19:33:17 +00:00
|
|
|
{"Mode 0: 320x240", 320, 240, 0},
|
|
|
|
{"Mode 1: 400x300", 400, 300, 1},
|
|
|
|
{"Mode 2: 512x384", 512, 384, 2},
|
|
|
|
{"Mode 3: 640x400", 640, 400, 3},
|
|
|
|
{"Mode 4: 640x480", 640, 480, 4},
|
|
|
|
{"Mode 5: 800x500", 800, 500, 5},
|
|
|
|
{"Mode 6: 800x600", 800, 600, 6},
|
|
|
|
{"Mode 7: 960x720", 960, 720, 7},
|
|
|
|
{"Mode 8: 1024x480", 1024, 480, 8},
|
|
|
|
{"Mode 9: 1024x640", 1024, 640, 9},
|
2013-05-01 15:15:35 +00:00
|
|
|
{"Mode 10: 1024x768", 1024, 768, 10},
|
|
|
|
{"Mode 11: 1152x768", 1152, 768, 11},
|
|
|
|
{"Mode 12: 1152x864", 1152, 864, 12},
|
|
|
|
{"Mode 13: 1280x800", 1280, 800, 13},
|
2017-02-25 19:33:17 +00:00
|
|
|
{"Mode 14: 1280x720", 1280, 720, 14},
|
2013-05-01 15:15:35 +00:00
|
|
|
{"Mode 15: 1280x960", 1280, 960, 15},
|
|
|
|
{"Mode 16: 1280x1024", 1280, 1024, 16},
|
|
|
|
{"Mode 17: 1366x768", 1366, 768, 17},
|
|
|
|
{"Mode 18: 1440x900", 1440, 900, 18},
|
|
|
|
{"Mode 19: 1600x1200", 1600, 1200, 19},
|
|
|
|
{"Mode 20: 1680x1050", 1680, 1050, 20},
|
|
|
|
{"Mode 21: 1920x1080", 1920, 1080, 21},
|
|
|
|
{"Mode 22: 1920x1200", 1920, 1200, 22},
|
|
|
|
{"Mode 23: 2048x1536", 2048, 1536, 23},
|
Add support for joystick/gamecontroller(axis/buttons/hats)/haptic.
By default joystick "in_joystick" and haptic feedback haptic ("joy_haptic_magnitude")
are disabled. And can be anabled by menu in options section. Joystick/Haptic
options are showed only if have found any.
By default axis is mapped in such way:
* Left X(joy_axis_leftx): sidemove
* Left Y(joy_axis_lefty): forwardmove
* Right X(joy_axis_rightx): yaw
* Right Y(joy_axis_righty): pitch
* Trigger Left(joy_axis_triggerleft): triggerleft
* Trigger Right(joy_axis_triggerright): triggerright
Joystick sensitivity varibales:
* joy_yawsensitivity,
* joy_pitchsensitivity,
* joy_forwardsensitivity,
* joy_sidesensitivity,
* joy_upsensitivity.
For change joystick axis mapping to gamecontoller axis export SDL_GAMECONTROLLERCONFIG before run,
e.g.: SDL_GAMECONTROLLERCONFIG='<joystick guid>,<joystick name>,leftx:a0,lefty:a1,rightx:a2,righty:a3,'
Add menu navigation by dpad and thresholds:
* Add threshold for axis (based on ioquake3), 0.15 by defaults.
* Navigate in menus by dpad (up/down, left/right, any joystick button for enter).
Defaults:
* in_joystick "0.0"
* joy_haptic_magnitude "0.0"
* joy_axis_leftx "sidemove"
* joy_axis_leftx_threshold "0.15"
* joy_axis_lefty "forwardmove"
* joy_axis_lefty_threshold "0.15"
* joy_axis_rightx "yaw"
* joy_axis_rightx_threshold "0.15"
* joy_axis_righty "pitch"
* joy_axis_righty_threshold "0.15"
* joy_axis_triggerleft "triggerleft"
* joy_axis_triggerleft_threshold "0.15"
* joy_axis_triggerright "triggerright"
* joy_axis_triggerright_threshold "0.15"
* joy_forwardsensitivity "1.0"
* joy_pitchsensitivity "1.0"
* joy_sidesensitivity "1.0"
* joy_upsensitivity "1.0"
* joy_yawsensitivity "1.0"
2017-09-16 11:23:12 +00:00
|
|
|
{"Mode 24: 2560x1080", 2560, 1080, 24},
|
|
|
|
{"Mode 25: 2560x1440", 2560, 1440, 25},
|
|
|
|
{"Mode 26: 2560x1600", 2560, 1600, 26},
|
|
|
|
{"Mode 27: 3440x1440", 3440, 1440, 27},
|
|
|
|
{"Mode 28: 3840x1600", 3840, 1600, 28},
|
|
|
|
{"Mode 29: 3840x2160", 3840, 2160, 29},
|
|
|
|
{"Mode 30: 4096x2160", 4096, 2160, 30},
|
|
|
|
{"Mode 31: 5120x2880", 5120, 2880, 31},
|
2013-05-01 15:15:35 +00:00
|
|
|
};
|
|
|
|
|
2012-06-08 11:01:56 +00:00
|
|
|
#define VID_NUM_MODES (sizeof(vid_modes) / sizeof(vid_modes[0]))
|
2009-03-05 09:03:26 +00:00
|
|
|
|
|
|
|
/*
|
2018-07-31 09:54:35 +00:00
|
|
|
* Callback function for the 'vid_listmodes' cmd.
|
2010-10-18 14:56:30 +00:00
|
|
|
*/
|
2017-02-25 19:33:17 +00:00
|
|
|
void
|
|
|
|
VID_ListModes_f(void)
|
|
|
|
{
|
|
|
|
int i;
|
2018-07-31 09:54:35 +00:00
|
|
|
|
2018-01-09 13:03:45 +00:00
|
|
|
Com_Printf("Supported video modes (r_mode):\n");
|
2018-07-31 09:54:35 +00:00
|
|
|
|
|
|
|
for (i = 0; i < VID_NUM_MODES; ++i)
|
2017-02-25 19:33:17 +00:00
|
|
|
{
|
|
|
|
Com_Printf(" %s\n", vid_modes[i].description);
|
|
|
|
}
|
2018-01-09 08:25:29 +00:00
|
|
|
Com_Printf(" Mode -1: r_customwidth x r_customheight\n");
|
2017-02-25 19:33:17 +00:00
|
|
|
}
|
|
|
|
|
2018-07-31 09:54:35 +00:00
|
|
|
/*
|
|
|
|
* Returns informations about the given mode.
|
|
|
|
*/
|
2010-10-18 14:56:30 +00:00
|
|
|
qboolean
|
2012-06-08 11:01:56 +00:00
|
|
|
VID_GetModeInfo(int *width, int *height, int mode)
|
2009-03-05 09:03:26 +00:00
|
|
|
{
|
2012-06-08 11:01:56 +00:00
|
|
|
if ((mode < 0) || (mode >= VID_NUM_MODES))
|
2010-10-18 14:56:30 +00:00
|
|
|
{
|
2012-06-08 11:01:56 +00:00
|
|
|
return false;
|
2010-10-18 14:56:30 +00:00
|
|
|
}
|
2009-03-05 09:03:26 +00:00
|
|
|
|
2012-06-08 11:01:56 +00:00
|
|
|
*width = vid_modes[mode].width;
|
|
|
|
*height = vid_modes[mode].height;
|
2009-03-05 09:03:26 +00:00
|
|
|
|
2012-06-08 11:01:56 +00:00
|
|
|
return true;
|
2009-03-05 09:03:26 +00:00
|
|
|
}
|
|
|
|
|
2018-07-31 09:54:35 +00:00
|
|
|
// --------
|
|
|
|
|
|
|
|
// Renderer load, restart and shutdown
|
|
|
|
// -----------------------------------
|
|
|
|
|
|
|
|
// Global console variables.
|
|
|
|
cvar_t *vid_gamma;
|
|
|
|
cvar_t *vid_fullscreen;
|
|
|
|
cvar_t *vid_renderer;
|
|
|
|
|
|
|
|
// Global video state, used throughout the client.
|
|
|
|
viddef_t viddef;
|
|
|
|
|
|
|
|
// Struct with the pointers exported by the renderer.
|
|
|
|
refexport_t re;
|
|
|
|
|
|
|
|
// Handle / pointer the the loaded renderer DLL.
|
|
|
|
void *reflib_handle = NULL;
|
|
|
|
|
|
|
|
// Is a renderer loaded and active?
|
|
|
|
qboolean ref_active = false;
|
2009-03-05 09:03:26 +00:00
|
|
|
|
|
|
|
/*
|
2018-07-31 09:54:35 +00:00
|
|
|
* FIXME: Not with vid_fullscreen...
|
2010-10-18 14:56:30 +00:00
|
|
|
*/
|
|
|
|
void
|
2018-07-31 09:54:35 +00:00
|
|
|
VID_Restart_f(void)
|
2009-03-05 09:03:26 +00:00
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
vid_fullscreen->modified = true;
|
2009-03-05 09:03:26 +00:00
|
|
|
}
|
|
|
|
|
2018-07-31 09:54:35 +00:00
|
|
|
/*
|
|
|
|
* FIXME: This is only used by the softrenderer. Remove it.
|
|
|
|
*/
|
2010-10-18 14:56:30 +00:00
|
|
|
void
|
2018-07-31 09:54:35 +00:00
|
|
|
VID_NewWindow(int width, int height)
|
2009-03-05 09:03:26 +00:00
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
viddef.width = width;
|
|
|
|
viddef.height = height;
|
2009-03-05 09:03:26 +00:00
|
|
|
}
|
|
|
|
|
2018-07-31 09:54:35 +00:00
|
|
|
/*
|
|
|
|
* Shuts the renderer down and unloads it.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
VID_ShutdownRenderer(void)
|
Screenshots can be tga, bmp, png, jpg now, using stb_image_write.h
the screenshot command now supports the filetype as optional argument
(just "screenshot" will use tga like before):
"screenshot png" will save the screenshot as PNG, same with jpg, png
and tga.
For jpg, you can even specify the quality, like "screenshot jpg 90"
(the Quality is between 1 and 100, like with libjpeg).
To reduce duplicated code, I addeed Vid_WriteScreenshot() to refimport_t
and implement most of it in the client (vid.c).
The renderer still fetches the raw image data from OpenGL or whatever
and then calls re.VidWriteScreenshot() which will write it to disk in
the format requested by the user.
2017-03-12 02:28:06 +00:00
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
if (ref_active)
|
Screenshots can be tga, bmp, png, jpg now, using stb_image_write.h
the screenshot command now supports the filetype as optional argument
(just "screenshot" will use tga like before):
"screenshot png" will save the screenshot as PNG, same with jpg, png
and tga.
For jpg, you can even specify the quality, like "screenshot jpg 90"
(the Quality is between 1 and 100, like with libjpeg).
To reduce duplicated code, I addeed Vid_WriteScreenshot() to refimport_t
and implement most of it in the client (vid.c).
The renderer still fetches the raw image data from OpenGL or whatever
and then calls re.VidWriteScreenshot() which will write it to disk in
the format requested by the user.
2017-03-12 02:28:06 +00:00
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
/* Shut down the renderer */
|
|
|
|
re.Shutdown();
|
|
|
|
Sys_FreeLibrary(reflib_handle);
|
|
|
|
reflib_handle = NULL;
|
|
|
|
memset(&re, 0, sizeof(re));
|
Screenshots can be tga, bmp, png, jpg now, using stb_image_write.h
the screenshot command now supports the filetype as optional argument
(just "screenshot" will use tga like before):
"screenshot png" will save the screenshot as PNG, same with jpg, png
and tga.
For jpg, you can even specify the quality, like "screenshot jpg 90"
(the Quality is between 1 and 100, like with libjpeg).
To reduce duplicated code, I addeed Vid_WriteScreenshot() to refimport_t
and implement most of it in the client (vid.c).
The renderer still fetches the raw image data from OpenGL or whatever
and then calls re.VidWriteScreenshot() which will write it to disk in
the format requested by the user.
2017-03-12 02:28:06 +00:00
|
|
|
}
|
|
|
|
|
2018-07-31 09:54:35 +00:00
|
|
|
// Declare the refresher as inactive
|
|
|
|
ref_active = false;
|
Screenshots can be tga, bmp, png, jpg now, using stb_image_write.h
the screenshot command now supports the filetype as optional argument
(just "screenshot" will use tga like before):
"screenshot png" will save the screenshot as PNG, same with jpg, png
and tga.
For jpg, you can even specify the quality, like "screenshot jpg 90"
(the Quality is between 1 and 100, like with libjpeg).
To reduce duplicated code, I addeed Vid_WriteScreenshot() to refimport_t
and implement most of it in the client (vid.c).
The renderer still fetches the raw image data from OpenGL or whatever
and then calls re.VidWriteScreenshot() which will write it to disk in
the format requested by the user.
2017-03-12 02:28:06 +00:00
|
|
|
}
|
|
|
|
|
2018-07-31 09:54:35 +00:00
|
|
|
/*
|
|
|
|
* Loads and initializes a renderer.
|
|
|
|
*/
|
2016-10-01 22:40:52 +00:00
|
|
|
qboolean
|
2018-07-31 09:54:35 +00:00
|
|
|
VID_LoadRenderer(void)
|
2016-10-01 22:40:52 +00:00
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
refimport_t ri;
|
|
|
|
GetRefAPI_t GetRefAPI;
|
2016-10-01 22:40:52 +00:00
|
|
|
|
|
|
|
#ifdef __APPLE__
|
|
|
|
const char* lib_ext = "dylib";
|
|
|
|
#elif defined(_WIN32)
|
|
|
|
const char* lib_ext = "dll";
|
|
|
|
#else
|
|
|
|
const char* lib_ext = "so";
|
|
|
|
#endif
|
2018-07-31 09:54:35 +00:00
|
|
|
|
2017-04-02 02:05:38 +00:00
|
|
|
char reflib_name[64] = {0};
|
2016-10-01 22:40:52 +00:00
|
|
|
char reflib_path[MAX_OSPATH] = {0};
|
|
|
|
|
2018-07-31 09:54:35 +00:00
|
|
|
// If the refresher is already active we need
|
|
|
|
// to shut it down before loading a new one
|
2018-07-30 08:56:18 +00:00
|
|
|
VID_ShutdownRenderer();
|
2016-10-01 22:40:52 +00:00
|
|
|
|
2018-07-31 09:54:35 +00:00
|
|
|
// Log what we're doing.
|
2016-10-01 22:40:52 +00:00
|
|
|
Com_Printf("----- refresher initialization -----\n");
|
|
|
|
|
2017-04-08 22:00:36 +00:00
|
|
|
snprintf(reflib_name, sizeof(reflib_name), "ref_%s.%s", vid_renderer->string, lib_ext);
|
2017-04-02 02:05:38 +00:00
|
|
|
snprintf(reflib_path, sizeof(reflib_path), "%s%s", Sys_GetBinaryDir(), reflib_name);
|
2017-04-20 17:57:42 +00:00
|
|
|
Com_Printf("LoadLibrary(%s)\n", reflib_name);
|
2018-07-31 09:54:35 +00:00
|
|
|
|
|
|
|
// Mkay, let's load the requested renderer.
|
2016-10-01 22:40:52 +00:00
|
|
|
GetRefAPI = Sys_LoadLibrary(reflib_path, "GetRefAPI", &reflib_handle);
|
2018-07-31 09:54:35 +00:00
|
|
|
|
|
|
|
// Okay, we couldn't load it. It's up to the
|
|
|
|
// caller to recover from this.
|
|
|
|
if (GetRefAPI == NULL)
|
2016-10-01 22:40:52 +00:00
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
Com_Printf("Loading %s as renderer lib failed!", reflib_path);
|
2017-06-30 11:40:44 +00:00
|
|
|
|
2016-10-01 22:40:52 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-31 09:54:35 +00:00
|
|
|
// Fill in the struct exported to the renderer.
|
|
|
|
// FIXME: Do we really need all these?
|
2016-10-01 22:40:52 +00:00
|
|
|
ri.Cmd_AddCommand = Cmd_AddCommand;
|
|
|
|
ri.Cmd_Argc = Cmd_Argc;
|
|
|
|
ri.Cmd_Argv = Cmd_Argv;
|
|
|
|
ri.Cmd_ExecuteText = Cbuf_ExecuteText;
|
2018-07-31 09:54:35 +00:00
|
|
|
ri.Cmd_RemoveCommand = Cmd_RemoveCommand;
|
2016-12-19 23:25:45 +00:00
|
|
|
ri.Com_VPrintf = Com_VPrintf;
|
2016-10-01 22:40:52 +00:00
|
|
|
ri.Cvar_Get = Cvar_Get;
|
|
|
|
ri.Cvar_Set = Cvar_Set;
|
|
|
|
ri.Cvar_SetValue = Cvar_SetValue;
|
2018-07-31 09:54:35 +00:00
|
|
|
ri.FS_FreeFile = FS_FreeFile;
|
|
|
|
ri.FS_Gamedir = FS_Gamedir;
|
|
|
|
ri.FS_LoadFile = FS_LoadFile;
|
|
|
|
ri.GLimp_InitGraphics = GLimp_InitGraphics;
|
|
|
|
ri.Sys_Error = Com_Error;
|
2016-10-01 22:40:52 +00:00
|
|
|
ri.Vid_GetModeInfo = VID_GetModeInfo;
|
|
|
|
ri.Vid_MenuInit = VID_MenuInit;
|
|
|
|
ri.Vid_NewWindow = VID_NewWindow;
|
Screenshots can be tga, bmp, png, jpg now, using stb_image_write.h
the screenshot command now supports the filetype as optional argument
(just "screenshot" will use tga like before):
"screenshot png" will save the screenshot as PNG, same with jpg, png
and tga.
For jpg, you can even specify the quality, like "screenshot jpg 90"
(the Quality is between 1 and 100, like with libjpeg).
To reduce duplicated code, I addeed Vid_WriteScreenshot() to refimport_t
and implement most of it in the client (vid.c).
The renderer still fetches the raw image data from OpenGL or whatever
and then calls re.VidWriteScreenshot() which will write it to disk in
the format requested by the user.
2017-03-12 02:28:06 +00:00
|
|
|
ri.Vid_WriteScreenshot = VID_WriteScreenshot;
|
2016-10-01 22:40:52 +00:00
|
|
|
|
2018-07-31 09:54:35 +00:00
|
|
|
// Exchange our export struct with the renderers import struct.
|
|
|
|
re = GetRefAPI(ri);
|
2016-10-01 22:40:52 +00:00
|
|
|
|
2018-07-31 09:54:35 +00:00
|
|
|
// Declare the refresher as active.
|
2016-10-01 22:40:52 +00:00
|
|
|
ref_active = true;
|
|
|
|
|
2018-07-31 09:54:35 +00:00
|
|
|
// Let's check if we've got a compatible renderer.
|
2016-10-01 22:40:52 +00:00
|
|
|
if (re.api_version != API_VERSION)
|
|
|
|
{
|
2018-07-30 08:56:18 +00:00
|
|
|
VID_ShutdownRenderer();
|
2018-07-31 09:54:35 +00:00
|
|
|
|
|
|
|
Com_Printf("%s has incompatible api_version %d!\n", reflib_name, re.api_version);
|
|
|
|
|
|
|
|
return false;
|
2016-10-01 22:40:52 +00:00
|
|
|
}
|
|
|
|
|
2018-07-31 09:54:35 +00:00
|
|
|
// Everything seems okay, initialize it.
|
2016-12-21 18:42:36 +00:00
|
|
|
if (!re.Init())
|
2016-10-01 22:40:52 +00:00
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
VID_ShutdownRenderer();
|
|
|
|
|
2017-04-02 02:05:38 +00:00
|
|
|
Com_Printf("ERROR: Loading %s as rendering backend failed!\n", reflib_name);
|
2016-12-21 18:42:36 +00:00
|
|
|
Com_Printf("------------------------------------\n\n");
|
2018-07-31 09:54:35 +00:00
|
|
|
|
|
|
|
return false;
|
2016-10-01 22:40:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Ensure that all key states are cleared */
|
|
|
|
Key_MarkAllUp();
|
|
|
|
|
2017-04-02 02:05:38 +00:00
|
|
|
Com_Printf("Successfully loaded %s as rendering backend\n", reflib_name);
|
2016-10-01 22:40:52 +00:00
|
|
|
Com_Printf("------------------------------------\n\n");
|
2018-07-31 09:54:35 +00:00
|
|
|
|
2016-10-01 22:40:52 +00:00
|
|
|
return true;
|
|
|
|
}
|
2018-07-31 09:54:35 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Checks if a renderer changes was requested and executes it.
|
|
|
|
* Inclusive fallback through all renderers. :)
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
VID_CheckChanges(void)
|
|
|
|
{
|
|
|
|
// FIXME: Not with vid_fullscreen
|
|
|
|
if (vid_fullscreen->modified)
|
|
|
|
{
|
|
|
|
// Stop sound, because the clients blocks while
|
|
|
|
// we're reloading the renderer. The sound system
|
|
|
|
// would screw up it's internal timings.
|
|
|
|
S_StopAllSounds();
|
2016-10-01 22:40:52 +00:00
|
|
|
|
2018-07-31 09:54:35 +00:00
|
|
|
// Reset the client side of the renderer state.
|
|
|
|
cl.refresh_prepped = false;
|
|
|
|
cl.cinematicpalette_active = false;
|
|
|
|
|
|
|
|
// More or less blocks the client.
|
|
|
|
cls.disable_screen = true;
|
|
|
|
|
|
|
|
// Mkay, let's try our luck.
|
|
|
|
while (!VID_LoadRenderer())
|
|
|
|
{
|
|
|
|
// We try: gl3 -> gl1 -> soft.
|
|
|
|
if (strcmp(vid_renderer->string, "gl3") == 0)
|
|
|
|
{
|
|
|
|
Com_Printf("Retrying with gl1...\n");
|
|
|
|
Cvar_Set("vid_renderer", "gl1");
|
|
|
|
}
|
|
|
|
else if (strcmp(vid_renderer->string, "gl1") == 0)
|
|
|
|
{
|
|
|
|
Com_Printf("Retrying with soft...\n");
|
|
|
|
Cvar_Set("vid_renderer", "soft");
|
|
|
|
}
|
|
|
|
else if (strcmp("vid_renderer", "soft") == 0)
|
|
|
|
{
|
|
|
|
// Sorry, no usable renderer found.
|
|
|
|
Com_Error(ERR_FATAL, "No usable renderer found!\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// User forced something stupid.
|
|
|
|
Com_Printf("Retrying with gl3...\n");
|
|
|
|
Cvar_Set("vid_renderer", "gl3");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unblock the client.
|
|
|
|
cls.disable_screen = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initializes the video stuff.
|
|
|
|
*/
|
2010-10-18 14:56:30 +00:00
|
|
|
void
|
2018-07-31 09:54:35 +00:00
|
|
|
VID_Init(void)
|
2009-03-05 09:03:26 +00:00
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
// Console variables
|
|
|
|
vid_fullscreen = Cvar_Get("vid_gamma", "1.0", CVAR_ARCHIVE);
|
|
|
|
vid_fullscreen = Cvar_Get("vid_fullscreen", "0", CVAR_ARCHIVE);
|
|
|
|
vid_renderer = Cvar_Get("vid_renderer", "gl1", CVAR_ARCHIVE);
|
|
|
|
|
|
|
|
// Commands
|
|
|
|
Cmd_AddCommand("vid_restart", VID_Restart_f);
|
|
|
|
Cmd_AddCommand("vid_listmodes", VID_ListModes_f);
|
|
|
|
|
|
|
|
// Initializes the video backend. This is NOT the renderer
|
|
|
|
// itself, just the client side support stuff!
|
|
|
|
if (!GLimp_Init())
|
2009-03-05 09:03:26 +00:00
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
Com_Error(ERR_FATAL, "Couldn't initialize the graphics subsystem!\n");
|
2009-03-05 09:03:26 +00:00
|
|
|
}
|
2013-05-01 15:15:35 +00:00
|
|
|
|
2018-07-31 09:54:35 +00:00
|
|
|
// Load the renderer and get things going.
|
|
|
|
VID_CheckChanges();
|
2009-03-05 09:03:26 +00:00
|
|
|
}
|
2016-10-01 22:40:52 +00:00
|
|
|
|
2018-07-31 09:54:35 +00:00
|
|
|
/*
|
|
|
|
* Shuts the video stuff down.
|
|
|
|
*/
|
2018-07-30 08:56:18 +00:00
|
|
|
void
|
|
|
|
VID_Shutdown(void)
|
|
|
|
{
|
|
|
|
VID_ShutdownRenderer();
|
|
|
|
GLimp_Shutdown();
|
|
|
|
}
|
|
|
|
|
2018-07-31 09:54:35 +00:00
|
|
|
// ----
|
|
|
|
|
|
|
|
// Wrappers for the functions provided by the renderer libs.
|
|
|
|
// =========================================================
|
2016-10-01 22:40:52 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
R_BeginRegistration(char *map)
|
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
if (ref_active)
|
2016-10-01 22:40:52 +00:00
|
|
|
{
|
|
|
|
re.BeginRegistration(map);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct model_s*
|
|
|
|
R_RegisterModel(char *name)
|
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
if (ref_active)
|
2016-10-01 22:40:52 +00:00
|
|
|
{
|
|
|
|
return re.RegisterModel(name);
|
|
|
|
}
|
2018-07-31 09:54:35 +00:00
|
|
|
|
2016-10-01 22:40:52 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct image_s*
|
|
|
|
R_RegisterSkin(char *name)
|
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
if (ref_active)
|
2016-10-01 22:40:52 +00:00
|
|
|
{
|
|
|
|
return re.RegisterSkin(name);
|
|
|
|
}
|
2018-07-31 09:54:35 +00:00
|
|
|
|
2016-10-01 22:40:52 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
R_SetSky(char *name, float rotate, vec3_t axis)
|
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
if (ref_active)
|
2016-10-01 22:40:52 +00:00
|
|
|
{
|
|
|
|
re.SetSky(name, rotate, axis);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
R_EndRegistration(void)
|
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
if (ref_active)
|
2016-10-01 22:40:52 +00:00
|
|
|
{
|
|
|
|
re.EndRegistration();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
R_RenderFrame(refdef_t *fd)
|
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
if (ref_active)
|
2016-10-01 22:40:52 +00:00
|
|
|
{
|
|
|
|
re.RenderFrame(fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct image_s*
|
|
|
|
Draw_FindPic(char *name)
|
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
if (ref_active)
|
2016-10-01 22:40:52 +00:00
|
|
|
{
|
|
|
|
return re.DrawFindPic(name);
|
|
|
|
}
|
2018-07-31 09:54:35 +00:00
|
|
|
|
2016-10-01 22:40:52 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
Draw_GetPicSize(int *w, int *h, char *name)
|
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
if (ref_active)
|
2016-10-01 22:40:52 +00:00
|
|
|
{
|
|
|
|
re.DrawGetPicSize(w, h, name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Draw_StretchPic(int x, int y, int w, int h, char *name)
|
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
if (ref_active)
|
2016-10-01 22:40:52 +00:00
|
|
|
{
|
|
|
|
re.DrawStretchPic(x, y, w, h, name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Draw_PicScaled(int x, int y, char *pic, float factor)
|
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
if (ref_active)
|
2016-10-01 22:40:52 +00:00
|
|
|
{
|
|
|
|
re.DrawPicScaled(x, y, pic, factor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Draw_CharScaled(int x, int y, int num, float scale)
|
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
if (ref_active)
|
2016-10-01 22:40:52 +00:00
|
|
|
{
|
|
|
|
re.DrawCharScaled(x, y, num, scale);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Draw_TileClear(int x, int y, int w, int h, char *name)
|
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
if (ref_active)
|
2016-10-01 22:40:52 +00:00
|
|
|
{
|
|
|
|
re.DrawTileClear(x, y, w, h, name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Draw_Fill(int x, int y, int w, int h, int c)
|
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
if (ref_active)
|
2016-10-01 22:40:52 +00:00
|
|
|
{
|
|
|
|
re.DrawFill(x, y, w, h, c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Draw_FadeScreen(void)
|
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
if (ref_active)
|
2016-10-01 22:40:52 +00:00
|
|
|
{
|
|
|
|
re.DrawFadeScreen();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Draw_StretchRaw(int x, int y, int w, int h, int cols, int rows, byte *data)
|
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
if (ref_active)
|
2016-10-01 22:40:52 +00:00
|
|
|
{
|
|
|
|
re.DrawStretchRaw(x, y, w, h, cols, rows, data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
R_SetPalette(const unsigned char *palette)
|
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
if (ref_active)
|
2016-10-01 22:40:52 +00:00
|
|
|
{
|
|
|
|
re.SetPalette(palette);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
R_BeginFrame(float camera_separation)
|
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
if (ref_active)
|
2016-10-01 22:40:52 +00:00
|
|
|
{
|
|
|
|
re.BeginFrame(camera_separation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
R_EndFrame(void)
|
|
|
|
{
|
|
|
|
if(ref_active)
|
|
|
|
{
|
|
|
|
re.EndFrame();
|
|
|
|
}
|
|
|
|
}
|
2017-03-04 15:31:34 +00:00
|
|
|
|
|
|
|
qboolean
|
|
|
|
R_IsVSyncActive(void)
|
|
|
|
{
|
2018-07-31 09:54:35 +00:00
|
|
|
if (ref_active)
|
2017-03-04 15:31:34 +00:00
|
|
|
{
|
|
|
|
return re.IsVSyncActive();
|
|
|
|
}
|
2018-07-31 09:54:35 +00:00
|
|
|
|
2017-03-04 15:31:34 +00:00
|
|
|
return false;
|
|
|
|
}
|