d1d0d86fea
Fix sound source issues in Q3. Fix q2 air acceleration/prediction omission. Don't change console completion while typing (while that option is still possible). Shift+tab now cycles completion backwards (now ctrl+shift for cycle subconsoles). Allow a few things to ignore sv_pure - including csprogs files (which is useful for all the mods that come with the csprogs.dat distributed separately). clamp pitch values to the range documented by openal, to hopefully avoid error spam. add some colour coding to the text editor when shader files are being edited/viewed. Changed how overbrights are clamped on q3bsp. Added portalfboscale for explicit texture scales on portal/refract/reflect fbos. qc decompiler can now at least attempt to decompile qtest's qc. fteqccgui can now be pointed at a .pak file, and decompile the progs.dat inside. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5269 fc73d0e0-1445-4013-8a0c-d673dee63da5
436 lines
No EOL
12 KiB
C
436 lines
No EOL
12 KiB
C
#include "quakedef.h"
|
|
#ifdef MENU_NATIVECODE
|
|
static dllhandle_t *libmenu;
|
|
menu_export_t *mn_entry;
|
|
|
|
extern unsigned int r2d_be_flags;
|
|
#include "pr_common.h"
|
|
#include "shader.h"
|
|
#include "cl_master.h"
|
|
|
|
static int MN_CheckExtension(const char *extname)
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < QSG_Extensions_count; i++)
|
|
{
|
|
if (!strcmp(QSG_Extensions[i].name, extname))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
static void MN_LocalCmd(const char *text)
|
|
{
|
|
Cbuf_AddText(text, RESTRICT_LOCAL); //menus are implicitly trusted. latching and other stuff would be a nightmare otherwise.
|
|
}
|
|
static const char *MN_Cvar_String(const char *cvarname, qboolean effective)
|
|
{
|
|
cvar_t *cv = Cvar_FindVar(cvarname);
|
|
if (cv)
|
|
{ //some cvars don't change instantly, giving them (temporary) effective values that are different from their intended values.
|
|
if (cv->latched_string && !effective)
|
|
return cv->latched_string;
|
|
return cv->string;
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
static const char *MN_Cvar_GetDefault(const char *cvarname)
|
|
{
|
|
cvar_t *cv = Cvar_FindVar(cvarname);
|
|
if (cv)
|
|
return cv->defaultstr?cv->defaultstr:"";
|
|
else
|
|
return NULL;
|
|
}
|
|
static void MN_RegisterCvar(const char *cvarname, const char *defaulttext, unsigned int flags, const char *description)
|
|
{
|
|
Cvar_Get2(cvarname, defaulttext, flags, description, NULL);
|
|
}
|
|
void Cmd_DeleteAlias(const char *name);
|
|
static void MN_RegisterCommand(const char *commandname, const char *description)
|
|
{
|
|
Cmd_DeleteAlias(commandname); //menuqc has no real way to register commands, so it has a nasty habit of creating loads of weird awkward aliases.
|
|
Cmd_AddCommandD(commandname, NULL, description);
|
|
}
|
|
static int MN_GetServerState(void)
|
|
{
|
|
if (!sv.active)
|
|
return 0;
|
|
if (svs.allocated_client_slots <= 1)
|
|
return 1;
|
|
return 2;
|
|
}
|
|
static int MN_GetClientState(void)
|
|
{
|
|
if (cls.state >= ca_active)
|
|
return 2;
|
|
if (cls.state != ca_disconnected)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
static void MN_fclose(vfsfile_t *f)
|
|
{
|
|
VFS_CLOSE(f);
|
|
}
|
|
static shader_t *MN_CachePic(const char *picname)
|
|
{
|
|
return R2D_SafeCachePic(picname);
|
|
}
|
|
static qboolean MN_DrawGetImageSize(struct shader_s *pic, int *w, int *h)
|
|
{
|
|
return R_GetShaderSizes(pic, w, h, true)>0;
|
|
}
|
|
static void MN_DrawQuad(const vec2_t position[4], const vec2_t texcoords[4], shader_t *pic, const vec4_t rgba, unsigned int be_flags)
|
|
{
|
|
extern shader_t *shader_draw_fill, *shader_draw_fill_trans;
|
|
r2d_be_flags = be_flags;
|
|
if (!pic)
|
|
pic = rgba[3]==1?shader_draw_fill:shader_draw_fill_trans;
|
|
R2D_ImageColours(rgba[0], rgba[1], rgba[2], rgba[3]);
|
|
R2D_Image2dQuad(position, texcoords, pic);
|
|
r2d_be_flags = 0;
|
|
}
|
|
static float MN_DrawString(const vec2_t position, const char *text, struct font_s *font, float height, const vec4_t rgba, unsigned int be_flags)
|
|
{
|
|
float px, py, ix;
|
|
unsigned int codeflags, codepoint;
|
|
conchar_t buffer[2048], *str = buffer;
|
|
if (!font)
|
|
font = font_default;
|
|
|
|
COM_ParseFunString(CON_WHITEMASK, text, buffer, sizeof(buffer), false);
|
|
|
|
R2D_ImageColours(rgba[0], rgba[1], rgba[2], rgba[3]);
|
|
Font_BeginScaledString(font, position[0], position[1], height, height, &px, &py);
|
|
ix=px;
|
|
while(*str)
|
|
{
|
|
str = Font_Decode(str, &codeflags, &codepoint);
|
|
px = Font_DrawScaleChar(px, py, codeflags, codepoint);
|
|
}
|
|
Font_EndString(font);
|
|
return ((px-ix)*(float)vid.width)/(float)vid.rotpixelwidth;
|
|
}
|
|
static float MN_StringWidth(const char *text, struct font_s *font, float height)
|
|
{
|
|
float px, py;
|
|
conchar_t buffer[2048], *end;
|
|
if (!font)
|
|
font = font_default;
|
|
|
|
end = COM_ParseFunString(CON_WHITEMASK, text, buffer, sizeof(buffer), false);
|
|
|
|
Font_BeginScaledString(font, 0, 0, height, height, &px, &py);
|
|
px = Font_LineScaleWidth(buffer, end);
|
|
Font_EndString(font);
|
|
return (px * (float)vid.width) / (float)vid.rotpixelwidth;
|
|
}
|
|
static void MN_DrawSetClipArea(float x, float y, float width, float height)
|
|
{
|
|
srect_t srect;
|
|
if (R2D_Flush)
|
|
R2D_Flush();
|
|
|
|
srect.x = x / (float)vid.fbvwidth;
|
|
srect.y = y / (float)vid.fbvheight;
|
|
srect.width = width / (float)vid.fbvwidth;
|
|
srect.height = height / (float)vid.fbvheight;
|
|
srect.dmin = -99999;
|
|
srect.dmax = 99999;
|
|
srect.y = (1-srect.y) - srect.height;
|
|
BE_Scissor(&srect);
|
|
}
|
|
static void MN_DrawResetClipArea(void)
|
|
{
|
|
if (R2D_Flush)
|
|
R2D_Flush();
|
|
BE_Scissor(NULL);
|
|
}
|
|
static qboolean MN_SetKeyDest(qboolean focused)
|
|
{
|
|
qboolean ret = Key_Dest_Has(kdm_nmenu);
|
|
if (ret == focused)
|
|
return false;
|
|
if (focused)
|
|
{
|
|
if (key_dest_absolutemouse & kdm_nmenu)
|
|
{ //we're activating the mouse cursor now... make sure the position is actually current.
|
|
//FIXME: we should probably get the input code to do this for us when switching cursor modes.
|
|
struct menu_inputevent_args_s ev = {MIE_MOUSEABS, -1};
|
|
ev.mouse.screen[0] = (mousecursor_x * vid.width) / vid.pixelwidth;
|
|
ev.mouse.screen[1] = (mousecursor_y * vid.height) / vid.pixelheight;
|
|
mn_entry->InputEvent(ev);
|
|
}
|
|
Key_Dest_Add(kdm_nmenu);
|
|
}
|
|
else
|
|
Key_Dest_Remove(kdm_nmenu);
|
|
return true;
|
|
}
|
|
static int MN_GetKeyDest(void)
|
|
{
|
|
if (Key_Dest_Has(kdm_nmenu))
|
|
{
|
|
if (Key_Dest_Has_Higher(kdm_nmenu))
|
|
return -1;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
static int MN_SetMouseTarget(const char *cursorname, float hot_x, float hot_y, float scale)
|
|
{
|
|
if (cursorname)
|
|
{
|
|
struct key_cursor_s *m = &key_customcursor[kc_nmenu];
|
|
if (scale <= 0)
|
|
scale = 1;
|
|
if (!strcmp(m->name, cursorname) || m->hotspot[0] != hot_x || m->hotspot[1] != hot_y || m->scale != scale)
|
|
{
|
|
Q_strncpyz(m->name, cursorname, sizeof(m->name));
|
|
m->hotspot[0] = hot_x;
|
|
m->hotspot[1] = hot_y;
|
|
m->scale = scale;
|
|
m->dirty = true;
|
|
}
|
|
key_dest_absolutemouse |= kdm_nmenu;
|
|
}
|
|
else
|
|
key_dest_absolutemouse &= ~kdm_nmenu;
|
|
return true;
|
|
}
|
|
|
|
static model_t *MN_CacheModel(const char *name)
|
|
{
|
|
return Mod_ForName(name, MLV_SILENT);
|
|
}
|
|
static qboolean MN_GetModelSize(model_t *model, vec3_t out_mins, vec3_t out_maxs)
|
|
{
|
|
if (model)
|
|
{
|
|
while(model->loadstate == MLS_LOADING)
|
|
COM_WorkerPartialSync(model, &model->loadstate, MLS_LOADING);
|
|
|
|
VectorCopy(model->mins, out_mins);
|
|
VectorCopy(model->maxs, out_maxs);
|
|
return model->loadstate == MLS_LOADED;
|
|
}
|
|
VectorClear(out_mins);
|
|
VectorClear(out_maxs);
|
|
return false;
|
|
}
|
|
static void MN_RenderScene(menuscene_t *scene)
|
|
{
|
|
int i;
|
|
entity_t ent;
|
|
menuentity_t *e;
|
|
if (R2D_Flush)
|
|
R2D_Flush();
|
|
|
|
CL_ClearEntityLists();
|
|
memset(&ent, 0, sizeof(ent));
|
|
for (i = 0; i < scene->numentities; i++)
|
|
{
|
|
e = &scene->entlist[i];
|
|
ent.keynum = i;
|
|
ent.model = scene->entlist[i].model;
|
|
VectorCopy(e->matrix[0], ent.axis[0]); ent.origin[0] = e->matrix[0][3];
|
|
VectorCopy(e->matrix[1], ent.axis[1]); ent.origin[1] = e->matrix[1][3];
|
|
VectorCopy(e->matrix[2], ent.axis[2]); ent.origin[2] = e->matrix[2][3];
|
|
|
|
ent.scale = 1;
|
|
ent.framestate.g[FS_REG].frame[0] = e->frame[0];
|
|
ent.framestate.g[FS_REG].frame[1] = e->frame[1];
|
|
ent.framestate.g[FS_REG].lerpweight[1] = e->frameweight[0];
|
|
ent.framestate.g[FS_REG].lerpweight[0] = e->frameweight[1];
|
|
ent.framestate.g[FS_REG].frametime[0] = e->frametime[0];
|
|
ent.framestate.g[FS_REG].frametime[1] = e->frametime[1];
|
|
|
|
ent.playerindex = -1;
|
|
ent.topcolour = TOP_DEFAULT;
|
|
ent.bottomcolour = BOTTOM_DEFAULT;
|
|
Vector4Set(ent.shaderRGBAf, 1, 1, 1, 1);
|
|
VectorSet(ent.glowmod, 1, 1, 1);
|
|
#ifdef HEXEN2
|
|
ent.drawflags = SCALE_ORIGIN_ORIGIN;
|
|
ent.abslight = 0;
|
|
#endif
|
|
ent.skinnum = 0;
|
|
ent.fatness = 0;
|
|
ent.forcedshader = NULL;
|
|
ent.customskin = 0;
|
|
|
|
V_AddAxisEntity(&ent);
|
|
}
|
|
|
|
VectorCopy(scene->viewmatrix[0], r_refdef.viewaxis[0]); r_refdef.vieworg[0] = scene->viewmatrix[0][3];
|
|
VectorCopy(scene->viewmatrix[1], r_refdef.viewaxis[1]); r_refdef.vieworg[1] = scene->viewmatrix[1][3];
|
|
VectorCopy(scene->viewmatrix[2], r_refdef.viewaxis[2]); r_refdef.vieworg[2] = scene->viewmatrix[2][3];
|
|
|
|
r_refdef.viewangles[0] = -(atan2(r_refdef.viewaxis[0][2], sqrt(r_refdef.viewaxis[0][1]*r_refdef.viewaxis[0][1]+r_refdef.viewaxis[0][0]*r_refdef.viewaxis[0][0])) * 180 / M_PI);
|
|
r_refdef.viewangles[1] = (atan2(r_refdef.viewaxis[0][1], r_refdef.viewaxis[0][0]) * 180 / M_PI);
|
|
r_refdef.viewangles[2] = 0;
|
|
|
|
r_refdef.flags = 0;
|
|
if (scene->worldmodel && scene->worldmodel == cl.worldmodel)
|
|
r_refdef.flags &= ~RDF_NOWORLDMODEL;
|
|
else
|
|
r_refdef.flags |= RDF_NOWORLDMODEL;
|
|
r_refdef.fovv_x = r_refdef.fov_x = scene->fov[0];
|
|
r_refdef.fovv_y = r_refdef.fov_y = scene->fov[1];
|
|
r_refdef.vrect.x = scene->pos[0];
|
|
r_refdef.vrect.y = scene->pos[1];
|
|
r_refdef.vrect.width = scene->size[0];
|
|
r_refdef.vrect.height = scene->size[1];
|
|
r_refdef.time = scene->time;
|
|
r_refdef.useperspective = true;
|
|
r_refdef.mindist = bound(0.1, gl_mindist.value, 4);
|
|
r_refdef.maxdist = gl_maxdist.value;
|
|
r_refdef.playerview = &cl.playerview[0];
|
|
|
|
memset(&r_refdef.globalfog, 0, sizeof(r_refdef.globalfog));
|
|
r_refdef.areabitsknown = false;
|
|
|
|
R_RenderView();
|
|
r_refdef.playerview = NULL;
|
|
r_refdef.time = 0;
|
|
}
|
|
|
|
void MN_Shutdown(void)
|
|
{
|
|
Key_Dest_Remove(kdm_nmenu);
|
|
if (mn_entry)
|
|
{
|
|
mn_entry->Shutdown(MI_INIT);
|
|
mn_entry = NULL;
|
|
}
|
|
if (libmenu)
|
|
{
|
|
Sys_CloseLibrary(libmenu);
|
|
libmenu = NULL;
|
|
}
|
|
}
|
|
qboolean MN_Init(void)
|
|
{
|
|
menu_export_t *(QDECL *pGetMenuAPI) ( menu_import_t *import );
|
|
static menu_import_t imports =
|
|
{
|
|
NATIVEMENU_API_VERSION_MAX,
|
|
NULL,
|
|
|
|
MN_CheckExtension,
|
|
Host_Error,
|
|
Con_Printf,
|
|
Con_DPrintf,
|
|
MN_LocalCmd,
|
|
Cvar_VariableValue,
|
|
MN_Cvar_String,
|
|
MN_Cvar_GetDefault,
|
|
Cvar_SetNamed,
|
|
MN_RegisterCvar,
|
|
MN_RegisterCommand,
|
|
|
|
COM_ParseType,
|
|
|
|
MN_GetServerState,
|
|
MN_GetClientState,
|
|
S_LocalSound2,
|
|
|
|
// file input / search crap
|
|
FS_OpenVFS,
|
|
MN_fclose,
|
|
VFS_GETS,
|
|
VFS_PRINTF,
|
|
COM_EnumerateFiles,
|
|
|
|
// Drawing stuff
|
|
MN_DrawSetClipArea,
|
|
MN_DrawResetClipArea,
|
|
|
|
//pics
|
|
MN_CachePic,
|
|
MN_DrawGetImageSize,
|
|
MN_DrawQuad,
|
|
|
|
//strings
|
|
MN_DrawString,
|
|
MN_StringWidth,
|
|
Font_LoadFont,
|
|
Font_Free,
|
|
|
|
//3d stuff
|
|
MN_CacheModel,
|
|
MN_GetModelSize,
|
|
MN_RenderScene,
|
|
|
|
// Menu specific stuff
|
|
MN_SetKeyDest,
|
|
MN_GetKeyDest,
|
|
MN_SetMouseTarget,
|
|
Key_KeynumToString,
|
|
Key_StringToKeynum,
|
|
M_FindKeysForBind,
|
|
|
|
// Server browser stuff
|
|
Master_KeyForName,
|
|
Master_SortedServer,
|
|
Master_ReadKeyString,
|
|
Master_ReadKeyFloat,
|
|
|
|
Master_ClearMasks,
|
|
Master_SetMaskString,
|
|
Master_SetMaskInteger,
|
|
Master_SetSortField,
|
|
Master_SortServers,
|
|
MasterInfo_Refresh,
|
|
CL_QueryServers,
|
|
};
|
|
dllfunction_t funcs[] =
|
|
{
|
|
{(void*)&pGetMenuAPI, "GetMenuAPI"},
|
|
{NULL}
|
|
};
|
|
void *iterator = NULL;
|
|
char syspath[MAX_OSPATH];
|
|
char gamepath[MAX_QPATH];
|
|
|
|
while(COM_IteratePaths(&iterator, syspath, sizeof(syspath), gamepath, sizeof(gamepath)))
|
|
{
|
|
if (!com_nogamedirnativecode.ival)
|
|
libmenu = Sys_LoadLibrary(va("%smenu_"ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, syspath), funcs);
|
|
if (libmenu)
|
|
break;
|
|
|
|
if (host_parms.binarydir && !strchr(gamepath, '/') && !strchr(gamepath, '\\'))
|
|
libmenu = Sys_LoadLibrary(va("%smenu_%s_"ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, host_parms.binarydir, gamepath), funcs);
|
|
if (libmenu)
|
|
break;
|
|
|
|
//some build systems don't really know the cpu type.
|
|
if (host_parms.binarydir && !strchr(gamepath, '/') && !strchr(gamepath, '\\'))
|
|
libmenu = Sys_LoadLibrary(va("%smenu_%s" ARCH_DL_POSTFIX, host_parms.binarydir, gamepath), funcs);
|
|
if (libmenu)
|
|
break;
|
|
}
|
|
|
|
if (libmenu)
|
|
{
|
|
imports.engine_version = version_string();
|
|
|
|
key_dest_absolutemouse |= kdm_nmenu;
|
|
|
|
mn_entry = pGetMenuAPI (&imports);
|
|
if (mn_entry && mn_entry->api_version >= NATIVEMENU_API_VERSION_MIN && mn_entry->api_version <= NATIVEMENU_API_VERSION_MAX)
|
|
{
|
|
mn_entry->Init(0, vid.width, vid.height, vid.pixelwidth, vid.pixelheight);
|
|
return true;
|
|
}
|
|
else
|
|
mn_entry = NULL;
|
|
MN_Shutdown();
|
|
Sys_CloseLibrary(libmenu);
|
|
libmenu = NULL;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif |