fteqw/engine/client/m_native.c

436 lines
12 KiB
C
Raw Normal View History

#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