diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index d2e58a390..36838b182 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -484,7 +484,8 @@ qboolean CL_GetDemoMessage (void) //force the console up, we're done loading. Key_Dest_Remove(kdm_console); - Key_Dest_Remove(kdm_menu); + Key_Dest_Remove(kdm_gmenu); + Key_Dest_Remove(kdm_emenu); scr_con_current = 0; } @@ -2031,7 +2032,7 @@ void CL_QTVPoll (void) if (!sourcesmenu) { m_state = m_complex; - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); sourcesmenu = M_CreateMenu(0); MC_AddPicture(sourcesmenu, 16, 4, 32, 144, "gfx/qplaque.lmp"); diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 494d1c37b..40f49c830 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -3549,7 +3549,7 @@ void CL_FTP_f(void) void CL_Fog_f(void) { if ((cl.fog_locked && !Cmd_FromGamecode()) || Cmd_Argc() <= 1) - Con_Printf("Current fog %f (r:%f g:%f b:%f)\n", cl.fog.density, cl.fog.colour[0], cl.fog.colour[1], cl.fog.colour[2]); + Con_Printf("Current fog %f (r:%f g:%f b:%f, a:%f bias:%f)\n", cl.fog.density, cl.fog.colour[0], cl.fog.colour[1], cl.fog.colour[2], cl.fog.alpha, cl.fog.depthbias); else { CL_ResetFog(); @@ -3923,7 +3923,7 @@ void CL_Init (void) Cmd_AddCommand ("topten", NULL); - Cmd_AddCommand ("fog", CL_Fog_f); + Cmd_AddCommandD ("fog", CL_Fog_f, "fog "); Cmd_AddCommand ("kill", NULL); Cmd_AddCommand ("pause", NULL); Cmd_AddCommand ("say", CL_Say_f); @@ -4729,7 +4729,8 @@ double Host_Frame (double time) #ifdef VM_UI UI_MenuState() != 0 || #endif - Key_Dest_Has(kdm_menu) || + Key_Dest_Has(kdm_gmenu) || + Key_Dest_Has(kdm_emenu) || Key_Dest_Has(kdm_editor) || #ifdef _WIN32 !ActiveApp || @@ -5052,7 +5053,9 @@ void CL_StartCinematicOrMenu(void) UI_Start(); #endif +#ifdef MENU_DAT Cbuf_AddText("menu_restart\n", RESTRICT_LOCAL); +#endif Con_TPrintf ("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081 %s %sInitialized ^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\n", *fs_gamename.string?fs_gamename.string:"Nothing", com_installer?"Installer ":""); diff --git a/engine/client/cl_plugin.inc b/engine/client/cl_plugin.inc index 3e358cc87..ecc00a3fd 100644 --- a/engine/client/cl_plugin.inc +++ b/engine/client/cl_plugin.inc @@ -27,19 +27,19 @@ static qintptr_t VARGS Plug_Menu_Control(void *offset, quintptr_t mask, const qi Plug_Menu_Event(3, 0); menuplug = NULL; currentplug = oldplug; - Key_Dest_Remove(kdm_menu); + Key_Dest_Remove(kdm_emenu); } if (VM_LONG(arg[0]) != 1) return 1; //give us menu control menuplug = currentplug; - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); m_state = m_plugin; return 1; case 2: //weather it's us or not. return currentplug == menuplug && m_state == m_plugin; case 3: //weather a menu is active - return !!Key_Dest_Has(kdm_menu); + return !!Key_Dest_Has(kdm_emenu|kdm_gmenu); default: return 0; } @@ -288,7 +288,7 @@ static qintptr_t VARGS Plug_Draw_Character(void *offset, quintptr_t mask, const if (qrenderer == QR_NONE) return 0; Font_BeginString(font_default, arg[0], arg[1], &x, &y); - Font_DrawChar(x, y, CON_WHITEMASK | 0xe000 | (unsigned int)arg[2]); + Font_DrawChar(x, y, CON_WHITEMASK, 0xe000 | (unsigned int)arg[2]); Font_EndString(font_default); return 0; } @@ -307,7 +307,7 @@ static qintptr_t VARGS Plug_Draw_CharacterH(void *offset, quintptr_t mask, const if (!(flags & 2)) cmask |= 0xe000; Font_BeginScaledString(font_default, x, y, h, h, &x, &y); - Font_DrawScaleChar(x, y, cmask | charc); + Font_DrawScaleChar(x, y, cmask, charc); Font_EndString(font_default); return 0; } @@ -315,6 +315,7 @@ static qintptr_t VARGS Plug_Draw_String(void *offset, quintptr_t mask, const qin { int ipx, px, py; conchar_t buffer[2048], *str; + unsigned int codeflags, codepoint; if (qrenderer == QR_NONE) return 0; COM_ParseFunString(CON_WHITEMASK, VM_POINTER(arg[2]), buffer, sizeof(buffer), false); @@ -323,13 +324,13 @@ static qintptr_t VARGS Plug_Draw_String(void *offset, quintptr_t mask, const qin ipx = px; while(*str) { - if ((*str & CON_CHARMASK) == '\n') + str = Font_Decode(str, &codeflags, &codepoint); + if (codepoint == '\n') py += Font_CharHeight(); - else if ((*str & CON_CHARMASK) == '\r') + else if (codepoint == '\r') px = ipx; else - px = Font_DrawChar(px, py, *str); - str++; + px = Font_DrawChar(px, py, codeflags, codepoint); } Font_EndString(font_default); return 0; @@ -343,6 +344,7 @@ static qintptr_t VARGS Plug_Draw_StringH(void *offset, quintptr_t mask, const qi char *instr = VM_POINTER(arg[4]); float ipx; conchar_t buffer[2048], *str, cmask = CON_WHITEMASK; + unsigned int codeflags, codepoint; unsigned int parseflags = 0; if (qrenderer == QR_NONE) return 0; @@ -356,13 +358,13 @@ static qintptr_t VARGS Plug_Draw_StringH(void *offset, quintptr_t mask, const qi ipx = x; while(*str) { - if ((*str & CON_CHARMASK) == '\n') + str = Font_Decode(str, &codeflags, &codepoint); + if (codepoint == '\n') y += Font_CharScaleHeight(); - else if ((*str & CON_CHARMASK) == '\r') + else if (codepoint == '\r') x = ipx; else - x = Font_DrawScaleChar(x, y, *str); - str++; + x = Font_DrawScaleChar(x, y, codeflags, codepoint); } Font_EndString(font_default); return 0; @@ -1171,7 +1173,7 @@ void Plug_Client_Close(plugin_t *plug) if (menuplug == plug) { menuplug = NULL; - Key_Dest_Remove(kdm_menu); + Key_Dest_Remove(kdm_emenu); } if (protocolclientplugin == plug) { diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index 074f936d0..a7b02b743 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -703,13 +703,10 @@ void SCR_DrawCursor(void) //choose the cursor based upon the module that has primary focus if (key_dest_mask & key_dest_absolutemouse & (kdm_console|kdm_cwindows|kdm_editor)) cmod = kc_console; - else if ((key_dest_mask & key_dest_absolutemouse & kdm_menu)) - { - if (m_state == m_menu_dat) - cmod = kc_menu; - else - cmod = kc_console; - } + else if ((key_dest_mask & key_dest_absolutemouse & kdm_emenu)) + cmod = kc_console; + else if ((key_dest_mask & key_dest_absolutemouse & kdm_gmenu)) + cmod = kc_menu; else// if (key_dest_mask & key_dest_absolutemouse) cmod = prydoncursornum?kc_console:kc_game; @@ -778,9 +775,9 @@ void SCR_DrawCursor(void) { float x, y; Font_BeginScaledString(font_default, mousecursor_x, mousecursor_y, 8, 8, &x, &y); - x -= Font_CharWidth('+' | 0xe000 | CON_WHITEMASK)/2; + x -= Font_CharScaleWidth(CON_WHITEMASK, '+' | 0xe000)/2; y -= Font_CharHeight()/2; - Font_DrawScaleChar(x, y, '+' | 0xe000 | CON_WHITEMASK); + Font_DrawScaleChar(x, y, CON_WHITEMASK, '+' | 0xe000); Font_EndString(font_default); } } @@ -793,9 +790,9 @@ static void SCR_DrawSimMTouchCursor(void) if (multicursor_active[i]) { Font_BeginScaledString(font_default, multicursor_x[i], multicursor_y[i], 8, 8, &x, &y); - x -= Font_CharWidth('+' | 0xe000 | CON_WHITEMASK)/2; + x -= Font_CharScaleWidth(CON_WHITEMASK, '+' | 0xe000)/2; y -= Font_CharHeight()/2; - Font_DrawScaleChar(x, y, '+' | 0xe000 | CON_WHITEMASK); + Font_DrawScaleChar(x, y, CON_WHITEMASK, '+' | 0xe000); Font_EndString(font_default); } } @@ -1382,6 +1379,7 @@ void SCR_DrawNet (void) R2D_ScalePic (scr_vrect.x+64, scr_vrect.y, 64, 64, scr_net); } +//FIXME: no support for UTF-8 input void SCR_StringXY(char *str, float x, float y) { char *s2; @@ -1392,14 +1390,14 @@ void SCR_StringXY(char *str, float x, float y) if (x < 0) { for (s2 = str; *s2; s2++) - px -= Font_CharWidth(*s2); + px -= Font_CharWidth(CON_WHITEMASK, *s2); } if (y < 0) py += y*Font_CharHeight(); while (*str) - px = Font_DrawChar(px, py, CON_WHITEMASK|*str++); + px = Font_DrawChar(px, py, CON_WHITEMASK, *str++); Font_EndString(font_default); } @@ -1568,7 +1566,7 @@ void SCR_DrawPause (void) if (!cl.paused) return; - if (Key_Dest_Has(kdm_menu)) + if (Key_Dest_Has(kdm_emenu) || Key_Dest_Has(kdm_gmenu)) return; pic = R2D_SafeCachePic ("gfx/pause.lmp"); @@ -1911,7 +1909,7 @@ void SCR_SetUpToDrawConsole (void) // Key_Dest_Add(kdm_console); scr_conlines = scr_con_current = vid.height * fullscreenpercent; } - else if (!startuppending && !Key_Dest_Has(kdm_menu) && (!Key_Dest_Has(~((!con_stayhidden.ival?kdm_console:0)|kdm_game))) && SCR_GetLoadingStage() == LS_NONE && cls.state < ca_active && !Media_PlayingFullScreen() && !CSQC_UnconnectedOkay(false)) + else if (!startuppending && !Key_Dest_Has(kdm_emenu|kdm_gmenu) && (!Key_Dest_Has(~((!con_stayhidden.ival?kdm_console:0)|kdm_game))) && SCR_GetLoadingStage() == LS_NONE && cls.state < ca_active && !Media_PlayingFullScreen() && !CSQC_UnconnectedOkay(false)) { //go fullscreen if we're not doing anything if (con_curwindow && !cls.state) @@ -1996,7 +1994,7 @@ void SCR_DrawConsole (qboolean noback) { if (!scr_con_current) { - if (!Key_Dest_Has(kdm_console|kdm_menu)) + if (!Key_Dest_Has(kdm_console|kdm_gmenu|kdm_emenu)) Con_DrawNotify (); // only draw notify in game } if (scr_con_current || Key_Dest_Has(kdm_cwindows)) @@ -2574,10 +2572,10 @@ void SCR_DrawTwoDimensional(int uimenu, qboolean nohud) if (!consolefocused) SCR_DrawConsole (false); - M_Draw (uimenu); #ifdef MENU_DAT MP_Draw(); #endif + M_Draw (uimenu); //but if the console IS focused, then always show it infront. if (consolefocused) diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 76b4d268d..06847f0c2 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -244,30 +244,39 @@ int rtq2_grenade=P_INVALID, rtqw_railtrail=P_INVALID, - rtfte_lightning1=P_INVALID, - ptfte_lightning1_end=P_INVALID, - rtfte_lightning2=P_INVALID, - ptfte_lightning2_end=P_INVALID, - rtfte_lightning3=P_INVALID, - ptfte_lightning3_end=P_INVALID, ptfte_bullet=P_INVALID, ptfte_superbullet=P_INVALID; +typedef struct { + /*static stuff*/ + char *modelname; + char *beamparticles; + char *beamimpactparticle; + int bflags; + + /*cached stuff*/ + model_t *model; + int ef_beam; + int ef_impact; +} tentmodels_t; + typedef struct { + tentmodels_t *info; int entity; short tag; - qbyte active; +// short pad; +// qbyte pad; qbyte bflags; qbyte type; qbyte skin; unsigned int rflags; - struct model_s *model; + float endtime; float alpha; vec3_t start, end; vec3_t offset; //when attached, this is the offset from the owning entity. probably only z is meaningful. - int particleeffect; +// int particlecolour; //some effects have specific colours. which is weird. trailstate_t *trailstate; trailstate_t *emitstate; } beam_t; @@ -305,6 +314,30 @@ int cl_explosions_max; static int explosions_running; static int beams_running; +static tentmodels_t beamtypes[] = +{ + {"progs/bolt.mdl", "TE_LIGHTNING1", "TE_LIGHTNING1_END"}, + {"progs/bolt2.mdl", "TE_LIGHTNING2", "TE_LIGHTNING2_END"}, + {"progs/bolt3.mdl", "TE_LIGHTNING3", "TE_LIGHTNING3_END"}, + {"progs/beam.mdl", "te_beam", "te_beam_end"}, //a CTF addition, but has other potential uses, sadly. + + {"q2cl_mod_parasite_segment", "te_parasite_attack", "te_parasite_attack_end"}, + {"q2cl_mod_grapple_cable", "te_grapple_cable", "te_grapple_cable_end"}, + {"models/proj/beam/tris.md2", "te_heatbeam", "te_heatbeam_end"}, + + {"models/stltng2.mdl", "te_stream_lightning_small", NULL}, + {"models/stchain.mdl", "te_stream_chain", NULL}, + {"models/stsunsf1.mdl", "te_stream_sunstaff1", NULL}, + {"models/stsunsf2.mdl", NULL, NULL}, + {"models/stsunsf1.mdl", "te_stream_sunstaff2", NULL}, + {"models/stlghtng.mdl", "te_stream_lightning", NULL}, + {"models/stclrbm.mdl", "te_stream_colorbeam", NULL}, + {"models/stice.mdl", "te_stream_icechunks", NULL}, + {"models/stmedgaz.mdl", "te_stream_gaze", NULL}, + {"models/fambeam.mdl", "te_stream_famine", NULL}, +}; + + sfx_t *cl_sfx_wizhit; sfx_t *cl_sfx_knighthit; sfx_t *cl_sfx_tink1; @@ -578,16 +611,22 @@ void CL_RegisterParticles(void) #endif rtqw_railtrail = P_FindParticleType("TE_RAILTRAIL"); - rtfte_lightning1 = P_FindParticleType("TE_LIGHTNING1"); - ptfte_lightning1_end = P_FindParticleType("TE_LIGHTNING1_END"); - rtfte_lightning2 = P_FindParticleType("TE_LIGHTNING2"); - ptfte_lightning2_end = P_FindParticleType("TE_LIGHTNING2_END"); - rtfte_lightning3 = P_FindParticleType("TE_LIGHTNING3"); - ptfte_lightning3_end = P_FindParticleType("TE_LIGHTNING3_END"); ptfte_bullet = P_FindParticleType("TE_BULLET"); ptfte_superbullet = P_FindParticleType("TE_SUPERBULLET"); CL_RefreshCustomTEnts(); + + for (i = 0; i < countof(beamtypes); i++) + { + //we can normally expect the server to have precache_modeled these models, so any lookups should be just a lookup, and thus relatively cheap. + beamtypes[i].model = NULL; + beamtypes[i].ef_beam = beamtypes[i].beamparticles?P_FindParticleType(beamtypes[i].beamparticles):P_INVALID; + beamtypes[i].ef_impact = beamtypes[i].beamimpactparticle?P_FindParticleType(beamtypes[i].beamimpactparticle):P_INVALID; + } + + //FIXME + for (i = 0; i < cl_explosions_max; i++) + cl_explosions[i].model = NULL; } #ifdef Q2CLIENT @@ -595,24 +634,16 @@ enum { q2cl_mod_explode, q2cl_mod_smoke, q2cl_mod_flash, - q2cl_mod_parasite_segment, - q2cl_mod_grapple_cable, q2cl_mod_parasite_tip, q2cl_mod_explo4, q2cl_mod_bfg_explo, q2cl_mod_powerscreen, q2cl_mod_max }; -typedef struct { - char *modelname; - -} tentmodels_t; tentmodels_t q2tentmodels[q2cl_mod_max] = { {"models/objects/explode/tris.md2"}, {"models/objects/smoke/tris.md2"}, {"models/objects/flash/tris.md2"}, - {"models/monsters/parasite/segment/tris.md2"}, - {"models/ctf/segment/tris.md2"}, {"models/monsters/parasite/tip/tris.md2"}, {"models/objects/r_explode/tris.md2"}, {"sprites/s_bfg2.sp2"}, @@ -726,7 +757,7 @@ explosion_t *CL_AllocExplosion (vec3_t org) CL_ParseBeam ================= */ -beam_t *CL_NewBeam (int entity, int tag) +beam_t *CL_NewBeam (int entity, int tag, tentmodels_t *btype) { beam_t *b; int i; @@ -737,7 +768,7 @@ beam_t *CL_NewBeam (int entity, int tag) for (i=0, b=cl_beams; i < beams_running; i++, b++) if (b->entity == entity && b->tag == tag) { - b->active = true; + b->info = btype; return b; } } @@ -745,9 +776,9 @@ beam_t *CL_NewBeam (int entity, int tag) // find a free beam for (i=0, b=cl_beams; i < beams_running; i++, b++) { - if (!b->active) + if (!b->info) { - b->active = true; + b->info = btype; return b; } } @@ -765,7 +796,7 @@ beam_t *CL_NewBeam (int entity, int tag) } beams_running++; - cl_beams[i].active = true; + cl_beams[i].info = btype; return &cl_beams[i]; } @@ -773,7 +804,7 @@ beam_t *CL_NewBeam (int entity, int tag) } #define STREAM_ATTACHED 16 #define STREAM_TRANSLUCENT 32 -void CL_AddBeam (int tent, int ent, vec3_t start, vec3_t end) //fixme: use TE_ numbers instead of 0 - 5 +void CL_AddBeam (enum beamtype_e tent, int ent, vec3_t start, vec3_t end) //fixme: use TE_ numbers instead of 0 - 5 { beam_t *b; @@ -782,11 +813,11 @@ void CL_AddBeam (int tent, int ent, vec3_t start, vec3_t end) //fixme: use TE_ n int i; vec3_t impact, normal; vec3_t extra; - char *mname; + //zquake compat requires some parsing weirdness. switch(tent) { - case 0: + case BT_Q1LIGHTNING1: if (ent < 0 && ent >= -512) //a zquake concept. ent between -1 and -maxplayers is to be taken to be a railtrail from a particular player instead of a beam. { // TODO: add support for those finnicky colored railtrails... @@ -794,55 +825,27 @@ void CL_AddBeam (int tent, int ent, vec3_t start, vec3_t end) //fixme: use TE_ n P_ParticleTrailIndex(start, end, 208, 8, NULL); return; } - default: - mname = "progs/bolt.mdl"; - btype = rtfte_lightning1; - etype = ptfte_lightning1_end; break; - case 1: + case BT_Q1LIGHTNING2: if (ent < 0 && ent >= -MAX_CLIENTS) //based on the railgun concept - this adds a rogue style TE_BEAM effect. - { - case 5: - mname = "progs/beam.mdl"; //remember to precache! - btype = P_FindParticleType("te_beam"); - etype = P_FindParticleType("te_beam_end"); - } - else - { - mname = "progs/bolt2.mdl"; - btype = rtfte_lightning2; - etype = ptfte_lightning2_end; - } + tent = BT_Q1BEAM; break; - case 2: - mname = "progs/bolt3.mdl"; - btype = rtfte_lightning3; - etype = ptfte_lightning3_end; + default: break; -#ifdef Q2CLIENT - case 3: - mname = q2tentmodels[q2cl_mod_parasite_segment].modelname; - btype = P_FindParticleType("te_parasite_attack"); - etype = P_FindParticleType("te_parasite_attack_end"); - break; - case 4: - mname = q2tentmodels[q2cl_mod_grapple_cable].modelname; - btype = P_FindParticleType("te_grapple_cable"); - etype = P_FindParticleType("te_grapple_cable_end"); - break; - case 6: - mname = "models/proj/beam/tris.md2"; - btype = P_FindParticleType("te_heatbeam"); - etype = P_FindParticleType("te_heatbeam_end"); - break; -#endif } + btype = beamtypes[tent].ef_beam; + etype = beamtypes[tent].ef_impact; + /*don't bother loading the model if we have a particle effect for it instead*/ if (ruleset_allow_particle_lightning.ival && btype >= 0) m = NULL; else - m = Mod_ForName(mname, MLV_WARN); + { + m = beamtypes[tent].model; + if (!m) + m = beamtypes[tent].model = Mod_ForName(beamtypes[tent].modelname, MLV_WARN); + } if (m && m->loadstate != MLS_LOADED) CL_CheckOrEnqueDownloadFile(m->name, NULL, 0); @@ -878,7 +881,7 @@ void CL_AddBeam (int tent, int ent, vec3_t start, vec3_t end) //fixme: use TE_ n } } - b = CL_NewBeam(ent, -1); + b = CL_NewBeam(ent, -1, &beamtypes[tent]); if (!b) { Con_Printf ("beam list overflow!\n"); @@ -887,8 +890,7 @@ void CL_AddBeam (int tent, int ent, vec3_t start, vec3_t end) //fixme: use TE_ n b->rflags = RF_NOSHADOW; b->entity = ent; - b->model = m; - b->particleeffect = btype; + b->info = &beamtypes[tent]; b->tag = -1; b->bflags |= /*STREAM_ATTACHED|*/1; b->endtime = cl.time + 0.2; @@ -902,7 +904,7 @@ void CL_AddBeam (int tent, int ent, vec3_t start, vec3_t end) //fixme: use TE_ n if (cl_legacystains.ival) Surf_AddStain(end, -10, -10, -10, 20); } } -void CL_ParseBeam (int tent) +void CL_ParseBeam (enum beamtype_e tent) { int ent; vec3_t start, end; @@ -950,6 +952,7 @@ void CL_ParseStream (int type) int tag; float duration; int skin; + tentmodels_t *info; ent = MSGCL_ReadEntity(); flags = MSG_ReadByte(); @@ -968,7 +971,47 @@ void CL_ParseStream (int type) end[1] = MSG_ReadCoord(); end[2] = MSG_ReadCoord(); - b = CL_NewBeam(ent, tag); + switch(type) + { + case TEH2_STREAM_LIGHTNING_SMALL: + info = &beamtypes[BT_H2LIGHTNING_SMALL]; + flags |= 2; + break; + case TEH2_STREAM_LIGHTNING: + info = &beamtypes[BT_H2LIGHTNING]; + flags |= 2; + break; + case TEH2_STREAM_ICECHUNKS: + info = &beamtypes[BT_H2ICECHUNKS]; + flags |= 2; + if (cl_legacystains.ival) Surf_AddStain(end, -10, -10, 0, 20); + break; + case TEH2_STREAM_SUNSTAFF1: + info = &beamtypes[BT_H2SUNSTAFF1]; + break; + case TEH2_STREAM_SUNSTAFF2: + info = &beamtypes[BT_H2SUNSTAFF2]; + if (cl_legacystains.ival) Surf_AddStain(end, -10, -10, -10, 20); + break; + case TEH2_STREAM_COLORBEAM: + info = &beamtypes[BT_H2COLORBEAM]; + break; + case TEH2_STREAM_GAZE: + info = &beamtypes[BT_H2GAZE]; + break; + case TEH2_STREAM_FAMINE: + info = &beamtypes[BT_H2FAMINE]; + break; + case TEH2_STREAM_CHAIN: + info = &beamtypes[BT_H2CHAIN]; + break; + default: + Con_Printf("CL_ParseStream: type %i\n", type); + info = &beamtypes[BT_H2LIGHTNING]; + break; + } + + b = CL_NewBeam(ent, tag, info); if (!b) { Con_Printf ("beam list overflow!\n"); @@ -979,8 +1022,6 @@ void CL_ParseStream (int type) b->entity = ent; b->tag = tag; b->bflags = flags; - b->model = NULL; - b->particleeffect = -1; b->endtime = cl.time + duration; b->alpha = 1; b->skin = skin; @@ -998,30 +1039,13 @@ void CL_ParseStream (int type) } } + //special handling... switch(type) { - case TEH2_STREAM_LIGHTNING_SMALL: - b->model = Mod_ForName("models/stltng2.mdl", MLV_WARN); - b->bflags |= 2; - b->particleeffect = P_FindParticleType("te_stream_lightning_small"); - break; - case TEH2_STREAM_LIGHTNING: - b->model = Mod_ForName("models/stlghtng.mdl", MLV_WARN); - b->bflags |= 2; - b->particleeffect = P_FindParticleType("te_stream_lightning"); - break; - case TEH2_STREAM_ICECHUNKS: - b->model = Mod_ForName("models/stice.mdl", MLV_WARN); - b->bflags |= 2; - b->particleeffect = P_FindParticleType("te_stream_icechunks"); - if (cl_legacystains.ival) Surf_AddStain(end, -10, -10, 0, 20); - break; case TEH2_STREAM_SUNSTAFF1: - b->model = Mod_ForName("models/stsunsf1.mdl", MLV_WARN); - b->particleeffect = P_FindParticleType("te_stream_sunstaff1"); - if (b->particleeffect < 0) + if (info->ef_beam == P_INVALID) { - b2 = CL_NewBeam(ent, tag+128); + b2 = CL_NewBeam(ent, tag+128, &beamtypes[BT_H2SUNSTAFF1_SUB]); if (b2) { P_DelinkTrailstate(&b2->trailstate); @@ -1029,37 +1053,12 @@ void CL_ParseStream (int type) memcpy(b2, b, sizeof(*b2)); b2->trailstate = NULL; b2->emitstate = NULL; - b2->model = Mod_ForName("models/stsunsf2.mdl", MLV_WARN); b2->alpha = 0.5; b2->rflags = RF_TRANSLUCENT|RF_NOSHADOW; } } //FIXME: we don't add the blob corners+smoke break; - case TEH2_STREAM_SUNSTAFF2: - b->model = Mod_ForName("models/stsunsf1.mdl", MLV_WARN); - b->particleeffect = P_FindParticleType("te_stream_sunstaff2"); - if (cl_legacystains.ival) Surf_AddStain(end, -10, -10, -10, 20); - break; - case TEH2_STREAM_COLORBEAM: - b->model = Mod_ForName("models/stclrbm.mdl", MLV_WARN); - b->particleeffect = P_FindParticleType("te_stream_colorbeam"); - break; - case TEH2_STREAM_GAZE: - b->model = Mod_ForName("models/stmedgaz.mdl", MLV_WARN); - b->particleeffect = P_FindParticleType("te_stream_gaze"); - break; - case TEH2_STREAM_FAMINE: - b->model = Mod_ForName("models/fambeam.mdl", MLV_WARN); - b->particleeffect = P_FindParticleType("te_stream_famine"); - break; - case TEH2_STREAM_CHAIN: - b->model = Mod_ForName("models/stchain.mdl", MLV_WARN); - b->particleeffect = P_FindParticleType("te_stream_chain"); - break; - default: - Con_Printf("CL_ParseStream: type %i\n", type); - break; } } @@ -1470,11 +1469,13 @@ void CL_ParseTEnt (void) break; case TE_LIGHTNING1: // lightning bolts + CL_ParseBeam (BT_Q1LIGHTNING1); + break; case TE_LIGHTNING2: // lightning bolts - CL_ParseBeam (type - TE_LIGHTNING1); + CL_ParseBeam (BT_Q1LIGHTNING2); break; case TE_LIGHTNING3: // lightning bolts - CL_ParseBeam (2); + CL_ParseBeam (BT_Q1LIGHTNING3); break; case TE_LAVASPLASH: @@ -1546,7 +1547,7 @@ void CL_ParseTEnt (void) break; case TEQW_BEAM: - CL_ParseBeam (5); + CL_ParseBeam (BT_Q1BEAM); break; case TE_RAILTRAIL: @@ -2524,14 +2525,14 @@ void CLQ2_ParseTEnt (void) case Q2TE_PARASITE_ATTACK: case Q2TE_MEDIC_CABLE_ATTACK: - CL_ParseBeam (3); + CL_ParseBeam (BT_Q2PARASITE); break; case Q2TE_HEATBEAM: case Q2TE_MONSTER_HEATBEAM: - CL_ParseBeam (6); + CL_ParseBeam (BT_Q2HEATBEAM); break; case Q2TE_GRAPPLE_CABLE: - CL_ParseBeam (4); + CL_ParseBeam (BT_Q2GRAPPLE); MSG_ReadPos (pos); break; @@ -2544,7 +2545,7 @@ void CLQ2_ParseTEnt (void) pos2[0] = MSG_ReadCoord (); pos2[1] = MSG_ReadCoord (); pos2[2] = MSG_ReadCoord (); - CL_AddBeam(0, ent, pos, pos2); + CL_AddBeam(BT_Q1LIGHTNING1, ent, pos, pos2); break; @@ -3407,13 +3408,15 @@ void CL_UpdateBeams (void) float yaw, pitch; float forward, offset; int lastrunningbeam = -1; + tentmodels_t *type; extern cvar_t cl_truelightning, v_viewheight; // update lightning for (bnum=0, b=cl_beams; bnum < beams_running; bnum++, b++) { - if (!b->active) + type = b->info; + if (!type) continue; if (b->endtime < cl.time) @@ -3422,7 +3425,7 @@ void CL_UpdateBeams (void) { /*don't let lightning decay while paused*/ P_DelinkTrailstate(&b->trailstate); P_DelinkTrailstate(&b->emitstate); - b->active = false; + b->info = NULL; continue; } } @@ -3554,11 +3557,15 @@ void CL_UpdateBeams (void) pitch += 360; } - if (ruleset_allow_particle_lightning.ival || !b->model) - if (b->particleeffect >= 0 && !P_ParticleTrail(b->start, b->end, b->particleeffect, b->entity, NULL, &b->trailstate)) + if (ruleset_allow_particle_lightning.ival || !type->modelname) + if (type->ef_beam >= 0 && !P_ParticleTrail(b->start, b->end, type->ef_beam, b->entity, NULL, &b->trailstate)) continue; - if (!b->model) - continue; + if (!type->model) + { + type->model = type->modelname?Mod_ForName(type->modelname, MLV_WARN):NULL; + if (!type->model) + continue; + } // add new entities for the lightning VectorCopy (b->start, org); @@ -3579,7 +3586,7 @@ void CL_UpdateBeams (void) if (!ent) return; VectorCopy (org, ent->origin); - ent->model = b->model; + ent->model = type->model; ent->drawflags |= MLS_ABSLIGHT; ent->abslight = 64 + 128 * bound(0, cl_shaftlight.value, 1); ent->shaderRGBAf[3] = b->alpha; diff --git a/engine/client/cl_ui.c b/engine/client/cl_ui.c index ccd83ab4b..599db3e9c 100644 --- a/engine/client/cl_ui.c +++ b/engine/client/cl_ui.c @@ -1519,8 +1519,8 @@ void UI_Reset(void) int UI_MenuState(void) { - if (Key_Dest_Has(kdm_menu)) - { + if (Key_Dest_Has(kdm_gmenu) || Key_Dest_Has(kdm_emenu)) + { //engine's menus take precedence over q3's ui return false; } if (!uivm) diff --git a/engine/client/client.h b/engine/client/client.h index 69368b963..ef7405a67 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -1033,7 +1033,30 @@ void CL_ParseTEnt (qboolean nqprot); void CL_ParseTEnt (void); #endif void CL_UpdateTEnts (void); -void CL_AddBeam (int tent, int ent, vec3_t start, vec3_t end); + +enum beamtype_e +{ //these are internal ids, matching the beam table + BT_Q1LIGHTNING1, + BT_Q1LIGHTNING2, + BT_Q1LIGHTNING3, + BT_Q1BEAM, + + BT_Q2PARASITE, + BT_Q2GRAPPLE, + BT_Q2HEATBEAM, + + BT_H2LIGHTNING_SMALL, + BT_H2CHAIN, + BT_H2SUNSTAFF1, + BT_H2SUNSTAFF1_SUB, //inner beam hack + BT_H2SUNSTAFF2, //same model as 1, but different particle effect + BT_H2LIGHTNING, + BT_H2COLORBEAM, + BT_H2ICECHUNKS, + BT_H2GAZE, + BT_H2FAMINE, +}; +void CL_AddBeam (enum beamtype_e tent, int ent, vec3_t start, vec3_t end); void CL_ClearState (void); void CLQ2_ClearState(void); diff --git a/engine/client/console.c b/engine/client/console.c index 9e89b955c..95d686e32 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -1077,6 +1077,7 @@ int Con_DrawInput (console_t *con, qboolean focused, int left, int right, int y, conchar_t *cursor; conchar_t *cchar; qboolean cursorframe; + unsigned int codeflags, codepoint; int x; @@ -1152,44 +1153,52 @@ int Con_DrawInput (console_t *con, qboolean focused, int left, int right, int y, #endif cursorframe = ((int)(realtime*con_cursorspeed)&1); - for (lhs = 0, i = cursor - maskedtext-1; i >= 0; i--) + //FIXME: support tab somehow + for (lhs = 0, cchar = maskedtext-1; cchar < cursor; ) { - lhs += Font_CharWidth(maskedtext[i]); + cchar = Font_Decode(cchar, &codeflags, &codepoint); + lhs += Font_CharWidth(codeflags, codepoint); } - for (rhs = 0, i = cursor - maskedtext; maskedtext[i]; i++) + for (rhs = 0, cchar = cursor; *cchar; ) { - rhs += Font_CharWidth(maskedtext[i]); + cchar = Font_Decode(cchar, &codeflags, &codepoint); + rhs += Font_CharWidth(codeflags, codepoint); } //put the cursor in the middle x = (right-left)/2 + left; //move the line to the right if there's not enough text to touch the right hand side - if (x < right-rhs - Font_CharWidth(0xe000|11|CON_WHITEMASK)) - x = right - rhs - Font_CharWidth(0xe000|11|CON_WHITEMASK); + if (x < right-rhs - Font_CharWidth(CON_WHITEMASK, 0xe000|11)) + x = right - rhs - Font_CharWidth(CON_WHITEMASK, 0xe000|11); //if the left hand side is on the right of the left point (overrides right alignment) if (x > lhs + left) x = lhs + left; lhs = x - lhs; - for (cchar = maskedtext; cchar < cursor; cchar++) + for (cchar = maskedtext; cchar < cursor; ) { - lhs = Font_DrawChar(lhs, y, *cchar); + cchar = Font_Decode(cchar, &codeflags, &codepoint); + lhs = Font_DrawChar(lhs, y, codeflags, codepoint); } rhs = x; + Font_Decode(cursor, &codeflags, &codepoint); if (cursorframe) { // extern cvar_t com_parseutf8; // if (com_parseutf8.ival) // Font_DrawChar(rhs, y, (*cursor&~(CON_BGMASK|CON_FGMASK)) | (COLOR_BLUE<flags & CONF_NOTIFY_RIGHT) { - for (c = starts[lines]; c < ends[lines]; c++) + for (c = starts[lines]; c < ends[lines]; ) { - x += Font_CharWidth(*c); + c = Font_Decode(c, &codeflags, &codepoint); + x += Font_CharWidth(codeflags, codepoint); } x = (nw - x); } else if (con_centernotify.value) { - for (c = starts[lines]; c < ends[lines]; c++) + for (c = starts[lines]; c < ends[lines]; ) { - x += Font_CharWidth(*c); + c = Font_Decode(c, &codeflags, &codepoint); + x += Font_CharWidth(codeflags, codepoint); } x = (nw - x) / 2; } @@ -1487,7 +1499,7 @@ static int Con_DrawProgress(int left, int right, int y) extern int relitsurface; #endif - conchar_t dlbar[1024]; + conchar_t dlbar[1024], *chr; unsigned char progresspercenttext[128]; char *progresstext = NULL; char *txt; @@ -1495,6 +1507,7 @@ static int Con_DrawProgress(int left, int right, int y) int i, j; int barwidth, barleft; float progresspercent = 0; + unsigned int codeflags, codepoint; *progresspercenttext = 0; // draw the download bar @@ -1564,9 +1577,10 @@ static int Con_DrawProgress(int left, int right, int y) x = 0; COM_ParseFunString(CON_WHITEMASK, txt, dlbar, sizeof(dlbar), false); - for (i = 0; dlbar[i]; ) + for (i=0,chr = dlbar; *chr; ) { - x += Font_CharWidth(dlbar[i]); + chr = Font_Decode(chr, &codeflags, &codepoint); + x += Font_CharWidth(codeflags, codepoint); i++; } @@ -1574,11 +1588,11 @@ static int Con_DrawProgress(int left, int right, int y) if (x > (right - left)/3) { //truncate the file name and add ... - x += 3*Font_CharWidth('.'|CON_WHITEMASK); - while (x > (right - left)/3 && i > 0) + x += 3*Font_CharWidth(CON_WHITEMASK, '.'); + while (x > (right - left)/3) { - i--; - x -= Font_CharWidth(dlbar[i]); + chr = Font_DecodeReverse(chr, dlbar, &codeflags, &codepoint); + x -= Font_CharWidth(codeflags, codepoint); } dlbar[i++] = '.'|CON_WHITEMASK; @@ -1591,46 +1605,52 @@ static int Con_DrawProgress(int left, int right, int y) //add a couple chars dlbar[i] = ':'|CON_WHITEMASK; - x += Font_CharWidth(dlbar[i]); + x += Font_CharWidth(CON_WHITEMASK, ':'); i++; dlbar[i] = ' '|CON_WHITEMASK; - x += Font_CharWidth(dlbar[i]); + x += Font_CharWidth(CON_WHITEMASK, ' '); i++; COM_ParseFunString(CON_WHITEMASK, progresspercenttext, dlbar+i, sizeof(dlbar)-i*sizeof(conchar_t), false); - for (j = i, tw = 0; dlbar[j]; ) + for (chr = &dlbar[i], tw = 0; *chr; ) { - tw += Font_CharWidth(dlbar[j]); - j++; + chr = Font_Decode(chr, &codeflags, &codepoint); + tw += Font_CharWidth(codeflags, codepoint); } barwidth = (right-left) - (x + tw); //draw the right hand side x = right - tw; - for (j = i; dlbar[j]; j++) - x = Font_DrawChar(x, y, dlbar[j]); + for (chr = &dlbar[i]; *chr; ) + { + chr = Font_Decode(chr, &codeflags, &codepoint); + x = Font_DrawChar(x, y, codeflags, codepoint); + } //draw the left hand side x = left; - for (j = 0; j < i; j++) - x = Font_DrawChar(x, y, dlbar[j]); + for (chr = dlbar; chr < &dlbar[i]; ) + { + chr = Font_Decode(chr, &codeflags, &codepoint); + x = Font_DrawChar(x, y, codeflags, codepoint); + } //and in the middle we have lots of stuff - barwidth -= (Font_CharWidth(0xe080|CON_WHITEMASK) + Font_CharWidth(0xe082|CON_WHITEMASK)); - x = Font_DrawChar(x, y, 0xe080|CON_WHITEMASK); + barwidth -= (Font_CharWidth(CON_WHITEMASK, 0xe080) + Font_CharWidth(CON_WHITEMASK, 0xe082)); + x = Font_DrawChar(x, y, CON_WHITEMASK, 0xe080); barleft = x; for(;;) { - if (x + Font_CharWidth(0xe081|CON_WHITEMASK) > barleft+barwidth) + if (x + Font_CharWidth(CON_WHITEMASK, 0xe081) > barleft+barwidth) break; - x = Font_DrawChar(x, y, 0xe081|CON_WHITEMASK); + x = Font_DrawChar(x, y, CON_WHITEMASK, 0xe081); } - x = Font_DrawChar(x, y, 0xe082|CON_WHITEMASK); + x = Font_DrawChar(x, y, CON_WHITEMASK, 0xe082); if (progresspercent >= 0) - Font_DrawChar(barleft+(barwidth*progresspercent)/100 - Font_CharWidth(0xe083|CON_WHITEMASK)/2, y, 0xe083|CON_WHITEMASK); + Font_DrawChar(barleft+(barwidth*progresspercent)/100 - Font_CharWidth(CON_WHITEMASK, 0xe083)/2, y, CON_WHITEMASK, 0xe083); y += Font_CharHeight(); } @@ -1646,6 +1666,7 @@ int Con_DrawAlternateConsoles(int lines) int consshown = 0; console_t *con = &con_main, *om = con_mouseover; conchar_t buffer[512], *end, *start; + unsigned int codeflags, codepoint; for (con = &con_main; con; con = con->next) { @@ -1669,19 +1690,20 @@ int Con_DrawAlternateConsoles(int lines) end = COM_ParseFunString(CON_WHITEMASK, va("^&%c%i%s", ((con!=om)?'F':'B'), (con==con_current)+con->unseentext*4, txt), buffer, sizeof(buffer), false); lx = 0; - for (lx = x, start = buffer; start < end; start++) + for (lx = x, start = buffer; start < end; ) { - lx = Font_CharEndCoord(font_console, lx, *start); + start = Font_Decode(start, &codeflags, &codepoint); + lx = Font_CharEndCoord(font_console, lx, codeflags, codepoint); } if (lx > Font_ScreenWidth()) { x = 0; y += h; } - for (lx = x, start = buffer; start < end; start++) + for (lx = x, start = buffer; start < end; ) { - Font_DrawChar(lx, y, *start); - lx = Font_CharEndCoord(font_console, lx, *start); + start = Font_Decode(start, &codeflags, &codepoint); + lx = Font_DrawChar(lx, y, codeflags, codepoint); } lx += 8; if (mx >= x && mx < lx && my >= y && my < y+h) @@ -1701,12 +1723,12 @@ int Con_DrawAlternateConsoles(int lines) static int Con_DrawConsoleLines(console_t *con, conline_t *l, int sx, int ex, int y, int top, qboolean selactive, int selsx, int selex, int selsy, int seley) { int linecount; - int linelength; conchar_t *starts[64], *ends[sizeof(starts)/sizeof(starts[0])]; - conchar_t *s; + conchar_t *s, *e, *c; int i; int x; int charh = Font_CharHeight(); + unsigned int codeflags, codepoint; if (l != con->completionline) if (l != con->footerline) @@ -1715,7 +1737,7 @@ static int Con_DrawConsoleLines(console_t *con, conline_t *l, int sx, int ex, in y -= 8; // draw arrows to show the buffer is backscrolled for (x = sx ; x 0) { s = starts[linecount]; - linelength = ends[linecount] - s; + e = ends[linecount]; y -= Font_CharHeight(); @@ -1877,12 +1899,15 @@ static int Con_DrawConsoleLines(console_t *con, conline_t *l, int sx, int ex, in int send; sstart = sx+picw; send = sstart; - for (i = 0; i < linelength; i++) - send = Font_CharEndCoord(font_console, send, s[i]); + for (c = s; c < e; ) + { + c = Font_Decode(c, &codeflags, &codepoint); + send = Font_CharEndCoord(font_console, send, codeflags, codepoint); + } //show something on blank lines if (send == sstart) - send = Font_CharEndCoord(font_console, send, ' '); + send = Font_CharEndCoord(font_console, send, CON_WHITEMASK, ' '); if (y+charh >= seley && y < selsy) { //if they're both on the same line, make sure sx is to the left of ex, so our stuff makes sense @@ -1897,9 +1922,10 @@ static int Con_DrawConsoleLines(console_t *con, conline_t *l, int sx, int ex, in if (y+charh >= seley) { send = sstart; - for (i = 0; i < linelength; ) + for (c = s; c < e; ) { - send = Font_CharEndCoord(font_console, send, s[i++]); + c = Font_Decode(c, &codeflags, &codepoint); + send = Font_CharEndCoord(font_console, send, codeflags, codepoint); if (send > selex) break; @@ -1907,23 +1933,25 @@ static int Con_DrawConsoleLines(console_t *con, conline_t *l, int sx, int ex, in con->selendline = l; if (s) - con->selendoffset = (s+i) - (conchar_t*)(l+1); + con->selendoffset = c - (conchar_t*)(l+1); else con->selendoffset = 0; } if (y < selsy) { - for (i = 0; i < linelength; i++) + for (c = s; c < e; ) { - x = Font_CharEndCoord(font_console, sstart, s[i]); + Font_Decode(c, &codeflags, &codepoint); + x = Font_CharEndCoord(font_console, sstart, codeflags, codepoint); if (x > selsx) break; + c = Font_Decode(c, &codeflags, &codepoint); sstart = x; } con->selstartline = l; if (s) - con->selstartoffset = (s+i) - (conchar_t*)(l+1); + con->selstartoffset = c - (conchar_t*)(l+1); else con->selstartoffset = 0; } @@ -1942,7 +1970,7 @@ static int Con_DrawConsoleLines(console_t *con, conline_t *l, int sx, int ex, in R2D_ImageColours(1.0, 1.0, 1.0, 1.0); x = sx + picw; - Font_LineDraw(x, y, s, s+linelength); + Font_LineDraw(x, y, s, e); if (y < top) break; @@ -2092,10 +2120,11 @@ void Con_DrawConsole (int lines, qboolean noback) int i; Font_BeginString(font_console, vid.width, lines, &x, &y); y -= Font_CharHeight(); + //assumption: version == ascii for (i = 0; version[i]; i++) - x -= Font_CharWidth(version[i] | CON_WHITEMASK|CON_HALFALPHA); + x -= Font_CharWidth(CON_WHITEMASK|CON_HALFALPHA, version[i]); for (i = 0; version[i]; i++) - x = Font_DrawChar(x, y, version[i] | CON_WHITEMASK|CON_HALFALPHA); + x = Font_DrawChar(x, y, CON_WHITEMASK|CON_HALFALPHA, version[i]); } Font_EndString(font_console); diff --git a/engine/client/keys.c b/engine/client/keys.c index 2585bde28..99ef04b22 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -2036,7 +2036,7 @@ qboolean Key_MouseShouldBeFree(void) // if (!ActiveApp) // return true; - if (Key_Dest_Has(kdm_menu)) + if (Key_Dest_Has(kdm_emenu)) { if (m_state == m_complex || m_state == m_plugin /*|| m_state == m_menu_dat*/) return true; @@ -2178,8 +2178,12 @@ void Key_Event (int devid, int key, unsigned int unicode, qboolean down) else if (Key_Dest_Has(kdm_editor)) Editor_Key (key, unicode); #endif - else if (Key_Dest_Has(kdm_menu)) + else if (Key_Dest_Has(kdm_emenu)) M_Keydown (key, unicode); +#ifdef MENU_DAT + else if (Key_Dest_Has(kdm_gmenu)) + MP_Keydown (key, unicode); +#endif else if (Key_Dest_Has(kdm_message)) Key_Dest_Remove(kdm_message); else @@ -2219,8 +2223,12 @@ void Key_Event (int devid, int key, unsigned int unicode, qboolean down) Key_ConsoleRelease(con, key, unicode); } } - if (Key_Dest_Has(kdm_menu)) + if (Key_Dest_Has(kdm_emenu)) M_Keyup (key, unicode); +#ifdef MENU_DAT + if (Key_Dest_Has(kdm_gmenu)) + MP_Keyup (key, unicode); +#endif #ifndef NOMEDIA if (Media_PlayingFullScreen()) Media_Send_KeyEvent(NULL, key, unicode, down?0:1); @@ -2297,11 +2305,18 @@ void Key_Event (int devid, int key, unsigned int unicode, qboolean down) return; } #endif - if (Key_Dest_Has(kdm_menu)) + if (Key_Dest_Has(kdm_emenu)) { M_Keydown (key, unicode); return; } +#ifdef MENU_DAT + if (Key_Dest_Has(kdm_gmenu)) + { + MP_Keydown (key, unicode); + return; + } +#endif if (Key_Dest_Has(kdm_message)) { Key_Message (key, unicode); diff --git a/engine/client/keys.h b/engine/client/keys.h index e127c3147..85782b514 100644 --- a/engine/client/keys.h +++ b/engine/client/keys.h @@ -179,10 +179,11 @@ typedef enum //highest has priority { kdm_game = 1u<<0, //should always be set kdm_message = 1u<<1, - kdm_menu = 1u<<2, - kdm_editor = 1u<<3, - kdm_console = 1u<<4, - kdm_cwindows = 1u<<5, + kdm_gmenu = 1u<<2, //menu.dat + kdm_emenu = 1u<<3, //engine's menus + kdm_editor = 1u<<4, + kdm_console = 1u<<5, + kdm_cwindows = 1u<<6, } keydestmask_t; //unsigned int Key_Dest_Get(void); //returns highest priority destination diff --git a/engine/client/m_download.c b/engine/client/m_download.c index 98725ca44..a397e69e0 100644 --- a/engine/client/m_download.c +++ b/engine/client/m_download.c @@ -763,7 +763,7 @@ void Menu_DownloadStuff_f (void) menu_t *menu; dlmenu_t *info; - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); m_state = m_complex; menu = M_CreateMenu(sizeof(dlmenu_t)); diff --git a/engine/client/m_items.c b/engine/client/m_items.c index c728b5e28..8dc16848a 100644 --- a/engine/client/m_items.c +++ b/engine/client/m_items.c @@ -567,12 +567,12 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, menu_t *menu option->slider.vx = x; x -= 8; Font_BeginString(font_default, x, y, &x, &y); - x = Font_DrawChar(x, y, 0xe080 | CON_WHITEMASK); + x = Font_DrawChar(x, y, CON_WHITEMASK, 0xe080); s = x; for (i=0 ; iexclusive && leaveprompts) + { + //this is WEIRD. + if (m == firstmenu) + link = &m->parent; + else + link = &m->child; + } + else + M_RemoveMenu(m); + } } void M_MenuPop_f (void) @@ -1701,7 +1712,7 @@ void M_Complex_Draw(void) if (!firstmenu) { - Key_Dest_Remove(kdm_menu); + Key_Dest_Remove(kdm_emenu); m_state = m_none; return; } @@ -2045,7 +2056,7 @@ qboolean MC_Main_Key (int key, menu_t *menu) //here purly to restart demos. if (!CL_TryingToConnect()) return true; - Key_Dest_Remove(kdm_menu); + Key_Dest_Remove(kdm_emenu); m_state = m_none; /* if (m_save_demonum != -1) { @@ -2118,7 +2129,7 @@ void M_Menu_Main_f (void) if (R_GetShaderSizes(R2D_SafeCachePic("pics/m_main_quit"), NULL, NULL, true) > 0) { m_state = m_complex; - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); mainm = M_CreateMenu(0); mainm->key = MC_Main_Key; @@ -2176,7 +2187,7 @@ void M_Menu_Main_f (void) return; m_state = m_complex; - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); mainm = M_CreateMenu(0); mainm->key = MC_Main_Key; @@ -2216,7 +2227,7 @@ void M_Menu_Main_f (void) { int y; m_state = m_complex; - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); mainm = M_CreateMenu(0); p = R2D_SafeCachePic("gfx/ttl_main.lmp"); @@ -2271,7 +2282,7 @@ void M_Menu_Main_f (void) else { m_state = m_complex; - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); mainm = M_CreateMenu(0); p = R2D_SafeCachePic("gfx/ttl_main.lmp"); diff --git a/engine/client/m_master.c b/engine/client/m_master.c index 4aab7fc74..e819e6fdd 100644 --- a/engine/client/m_master.c +++ b/engine/client/m_master.c @@ -721,7 +721,7 @@ doconnect: Cbuf_AddText("\n", RESTRICT_LOCAL); - M_RemoveAllMenus(); + M_RemoveAllMenus(true); return true; } else if (server && key == 'c' && ctrldown) //copy to clip @@ -1031,7 +1031,7 @@ void M_Menu_ServerList2_f(void) serverpreview = false; //in case it was lingering. Key_Dest_Remove(kdm_console); - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); m_state = m_complex; menu = M_CreateMenu(sizeof(serverlist_t)); @@ -1229,7 +1229,7 @@ void M_QuickConnect_f(void) menucustom_t *cust; menu_t *menu; - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); m_state = m_complex; MasterInfo_Refresh(); diff --git a/engine/client/m_mp3.c b/engine/client/m_mp3.c index cd8c69fb6..a1d3f8ef1 100644 --- a/engine/client/m_mp3.c +++ b/engine/client/m_mp3.c @@ -8,8 +8,8 @@ #include "shader.h" #if !defined(NOMEDIA) -#if defined(_WIN32) && !defined(WINRT) -#define WINAMP +#if defined(_WIN32) && !defined(WINRT) && !defined(NOMEDIAMENU) +//#define WINAMP #endif #if defined(_WIN32) && !defined(WINRT) #define WINAVI @@ -22,6 +22,8 @@ typedef struct mediatrack_s{ int length; struct mediatrack_s *next; } mediatrack_t; +qboolean media_fadeout; +float media_fadeouttime; static mediatrack_t currenttrack; int media_playing=true;//try to continue from the standard playlist @@ -56,9 +58,10 @@ void Media_Clear (void) Q_strncpyz(currenttrack.filename, "", sizeof(currenttrack.filename)); fakecdactive = false; media_playing = false; -#endif - S_Music_Clear(NULL); + media_fadeout = true; + media_fadeouttime = realtime; +#endif } //fake cd tracks. @@ -579,20 +582,67 @@ void Media_Next_f (void) +void Media_AddTrack(const char *fname) +{ + mediatrack_t *newtrack; + if (!*fname) + return; + for (newtrack = tracks; newtrack; newtrack = newtrack->next) + { + if (!strcmp(newtrack->filename, fname)) + return; //already added. ho hum + } + newtrack = Z_Malloc(sizeof(mediatrack_t)); + Q_strncpyz(newtrack->filename, fname, sizeof(newtrack->filename)); + Q_strncpyz(newtrack->nicename, COM_SkipPath(fname), sizeof(newtrack->nicename)); + newtrack->length = 0; + newtrack->next = tracks; + tracks = newtrack; + numtracks++; +} +void Media_RemoveTrack(const char *fname) +{ + mediatrack_t **link, *newtrack; + if (!*fname) + return; + for (link = &tracks; *link; link = &(*link)->next) + { + newtrack = *link; + if (!strcmp(newtrack->filename, fname)) + { + *link = newtrack->next; + Z_Free(newtrack); + numtracks--; + return; + } + } +} +void M_Media_Add_f (void) +{ + char *fname = Cmd_Argv(1); + + if (Cmd_Argc() == 1) + Con_Printf("%s \n", Cmd_Argv(0)); + else + Media_AddTrack(fname); +} +void M_Media_Remove_f (void) +{ + char *fname = Cmd_Argv(1); + + if (Cmd_Argc() == 1) + Con_Printf("%s \n", Cmd_Argv(0)); + else + Media_RemoveTrack(fname); +} + #ifndef NOMEDIAMENU -void M_Menu_Media_f (void) -{ - Key_Dest_Add(kdm_menu); - m_state = m_media; -} - void Media_LoadTrackNames (char *listname); -#define MEDIA_MIN -8 -#define MEDIA_VOLUME -8 -#define MEDIA_REWIND -7 +#define MEDIA_MIN -7 +#define MEDIA_VOLUME -7 #define MEDIA_FASTFORWARD -6 #define MEDIA_CLEARLIST -5 #define MEDIA_ADDTRACK -4 @@ -600,7 +650,7 @@ void Media_LoadTrackNames (char *listname); #define MEDIA_SHUFFLE -2 #define MEDIA_REPEAT -1 -void M_Media_Draw (void) +void M_Media_Draw (menu_t *menu) { mpic_t *p; mediatrack_t *track; @@ -609,9 +659,6 @@ void M_Media_Draw (void) #define MP_Hightlight(x,y,text,hl) (hl?M_PrintWhite(x, y, text):M_Print(x, y, text)) - p = R2D_SafeCachePic ("gfx/p_option.lmp"); - if (p) - M_DrawScalePic ( (320-p->width)/2, 4, 144, 24, p); if (!bgmvolume.value) M_Print (12, 32, "Not playing - no volume"); else if (!*currenttrack.nicename) @@ -634,18 +681,18 @@ void M_Media_Draw (void) M_Print (12, 40, currenttrack.nicename); } - op = selectedoption - (vid.height-52)/16; - if (op + (vid.height-52)/8>numtracks) - op = numtracks - (vid.height-52)/8; + y=52; + op = selectedoption - (vid.height-y)/16; + if (op + (vid.height-y)/8>numtracks) + op = numtracks - (vid.height-y)/8; if (op < MEDIA_MIN) op = MEDIA_MIN; - y=52; while(op < 0) { switch(op) { case MEDIA_VOLUME: - MP_Hightlight (12, y, "Volume", op == selectedoption); + MP_Hightlight (12, y, va("<< Volume %2i%% >>", (int)(100*bgmvolume.value)), op == selectedoption); y+=8; break; case MEDIA_CLEARLIST: @@ -653,11 +700,13 @@ void M_Media_Draw (void) y+=8; break; case MEDIA_FASTFORWARD: - MP_Hightlight (12, y, ">> Fast Forward", op == selectedoption); - y+=8; - break; - case MEDIA_REWIND: - MP_Hightlight (12, y, "<< Rewind", op == selectedoption); + { + float time, duration; + if (S_GetMusicInfo(0, &time, &duration)) + MP_Hightlight (12, y, va("<< %i:%02i / %i:%02i - %i%% >>", (int)time/60, (int)time%60, (int)duration/60, (int)duration%60, (int)(100*time/duration)), op == selectedoption); + else + MP_Hightlight (12, y, "<< skip >>", op == selectedoption); + } y+=8; break; case MEDIA_ADDTRACK: @@ -713,7 +762,7 @@ void M_Media_Draw (void) char compleatenamepath[MAX_OSPATH]; char compleatenamename[MAX_OSPATH]; qboolean compleatenamemultiple; -int QDECL Com_CompleatenameCallback(const char *name, qofs_t size, void *data, searchpathfuncs_t *spath) +int QDECL Com_CompleatenameCallback(const char *name, qofs_t size, time_t mtime, void *data, searchpathfuncs_t *spath) { if (*compleatenamename) compleatenamemultiple = true; @@ -739,17 +788,12 @@ void Com_CompleateOSFileName(char *name) strcpy(name, compleatenamename); } -void M_Media_Key (int key) +qboolean M_Media_Key (int key, menu_t *menu) { int dir; if (key == K_ESCAPE) { -#ifndef NOBUILTINMENUS - M_Menu_Main_f(); -#else - m_state = m_none; - Key_Dest_Remove(kdm_menu); -#endif + return false; } else if (key == K_RIGHTARROW || key == K_LEFTARROW) { @@ -766,6 +810,9 @@ void M_Media_Key (int key) bgmvolume.value = 1; Cvar_SetValue (&bgmvolume, bgmvolume.value); break; + case MEDIA_FASTFORWARD: + Media_Seek(15*dir); + break; default: if (selectedoption >= 0) Media_Next_f(); @@ -829,9 +876,6 @@ void M_Media_Key (int key) case MEDIA_FASTFORWARD: Media_Seek(15); break; - case MEDIA_REWIND: - Media_Seek(-15); - break; case MEDIA_CLEARLIST: { mediatrack_t *prevtrack; @@ -851,16 +895,9 @@ void M_Media_Key (int key) break; case MEDIA_ADDTRACK: if (*media_iofilename) - { - mediatrack_t *newtrack; - newtrack = Z_Malloc(sizeof(mediatrack_t)); - Q_strncpyz(newtrack->filename, media_iofilename, sizeof(newtrack->filename)); - Q_strncpyz(newtrack->nicename, COM_SkipPath(media_iofilename), sizeof(newtrack->nicename)); - newtrack->length = 0; - newtrack->next = tracks; - tracks = newtrack; - numtracks++; - } + Media_AddTrack(media_iofilename); + else + Cmd_ExecuteString("menu_mediafiles", RESTRICT_LOCAL); break; case MEDIA_ADDLIST: if (*media_iofilename) @@ -878,27 +915,34 @@ void M_Media_Key (int key) media_playing = true; nexttrack = selectedoption; Media_Next_f(); + return true; } - break; + return false; } + return true; } else { if (selectedoption == MEDIA_ADDLIST || selectedoption == MEDIA_ADDTRACK) { if (key == K_TAB) + { Com_CompleateOSFileName(media_iofilename); + return true; + } else if (key == K_BACKSPACE) { dir = strlen(media_iofilename); if (dir) media_iofilename[dir-1] = '\0'; + return true; } else if ((key >= 'a' && key <= 'z') || (key >= '0' && key <= '9') || key == '/' || key == '_' || key == '.' || key == ':') { dir = strlen(media_iofilename); media_iofilename[dir] = key; media_iofilename[dir+1] = '\0'; + return true; } } else if (selectedoption>=0) @@ -915,25 +959,40 @@ void M_Media_Key (int key) num++; } if (!tr) - return; + return false; if (key == K_BACKSPACE) { dir = strlen(tr->nicename); if (dir) tr->nicename[dir-1] = '\0'; + return true; } else if ((key >= 'a' && key <= 'z') || (key >= '0' && key <= '9') || key == '/' || key == '_' || key == '.' || key == ':' || key == '&' || key == '|' || key == '#' || key == '\'' || key == '\"' || key == '\\' || key == '*' || key == '@' || key == '!' || key == '(' || key == ')' || key == '%' || key == '^' || key == '?' || key == '[' || key == ']' || key == ';' || key == ':' || key == '+' || key == '-' || key == '=') { dir = strlen(tr->nicename); tr->nicename[dir] = key; tr->nicename[dir+1] = '\0'; + return true; } } } + return false; } +void M_Menu_Media_f (void) +{ + menu_t *menu; + m_state = m_complex; + Key_Dest_Add(kdm_emenu); + menu = M_CreateMenu(0); +// MC_AddPicture(menu, 16, 4, 32, 144, "gfx/qplaque.lmp"); + MC_AddCenterPicture(menu, 4, 24, "gfx/p_option.lmp"); + + menu->key = M_Media_Key; + menu->postdraw = M_Media_Draw; +} @@ -945,7 +1004,8 @@ void Media_LoadTrackNames (char *listname) char *filename; char *trackname; mediatrack_t *newtrack; - char *data = COM_LoadTempFile(listname); + size_t fsize; + char *data = COM_LoadTempFile(listname, &fsize); loadedtracknames=true; @@ -1032,21 +1092,33 @@ void Media_LoadTrackNames (char *listname) } #endif -//safeprints only. +//mixer is locked, its safe to do stuff, but try not to block +float Media_CrossFade(int musicchanel, float vol) +{ + if (media_fadeout) + { + float fadetime = 1; + float frac = (fadetime + media_fadeouttime - realtime)/fadetime; + vol *= frac; + } + return vol; +} + +//mixer is locked, its safe to do stuff, but try not to block char *Media_NextTrack(int musicchannelnum) { -#ifdef WINAMP - if (media_hijackwinamp.value) - { - WinAmp_Think(); - return NULL; - } -#endif if (bgmvolume.value <= 0 || !media_playing) return NULL; + if (media_fadeout) + { + if (S_Music_Playing(musicchannelnum)) + return NULL; //can't pick a new track until they've all stopped. + } + if (!fakecdactive) Media_EndedTrack(); + media_fadeout = false; #ifndef NOMEDIAMENU if (!loadedtracknames) @@ -2274,11 +2346,13 @@ qboolean Media_PlayFilm(char *name, qboolean enqueue) CDAudio_Stop(); SCR_EndLoadingPlaque(); - if (Key_Dest_Has(kdm_menu)) + if (Key_Dest_Has(kdm_emenu)) { - Key_Dest_Remove(kdm_menu); + Key_Dest_Remove(kdm_emenu); m_state = m_none; } + if (Key_Dest_Has(kdm_gmenu)) + Key_Dest_Remove(kdm_gmenu); //FIXME if (!Key_Dest_Has(kdm_console)) scr_con_current=0; return true; @@ -3228,7 +3302,7 @@ void Media_RecordDemo_f(void) Cmd_ShiftArgs(1, false); Media_RecordFilm_f(); scr_con_current=0; - Key_Dest_Remove(kdm_console|kdm_menu); + Key_Dest_Remove(kdm_console|kdm_emenu|kdm_gmenu); if (currentcapture_funcs) recordingdemo = true; @@ -3981,6 +4055,9 @@ void Media_Init(void) #endif Cvar_Register(&media_shuffle, "Media player things"); Cvar_Register(&media_repeat, "Media player things"); + Cmd_AddCommand ("media_add", M_Media_Add_f); + Cmd_AddCommand ("media_rmeove", M_Media_Remove_f); + Cmd_AddCommand ("menu_media", M_Menu_Media_f); } @@ -4209,6 +4286,7 @@ void Media_RecordAudioFrame (short *sample_buffer, int samples) {} void Media_StopRecordFilm_f (void) {} void Media_RecordFilm_f (void){} void M_Menu_Media_f (void) {} +float Media_CrossFade(int ch, float vol) {return vol;} char *Media_NextTrack(int musicchannelnum) {return NULL;} qboolean Media_PausedDemo(qboolean fortiming) {return false;} diff --git a/engine/client/m_multi.c b/engine/client/m_multi.c index 126c24315..73c97d7e3 100644 --- a/engine/client/m_multi.c +++ b/engine/client/m_multi.c @@ -18,7 +18,7 @@ void M_Menu_MultiPlayer_f (void) static menuresel_t resel; p = NULL; - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); m_state = m_complex; mgt = M_GameType(); @@ -442,7 +442,7 @@ void M_Menu_Setup_f (void) mpic_t *p; menucustom_t *cu; m_state = m_complex; - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); menu = M_CreateMenu(sizeof(setupmenu_t)); info = menu->data; @@ -468,7 +468,7 @@ void M_Menu_Setup_f (void) } #endif - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); m_state = m_complex; menu = M_CreateMenu(sizeof(setupmenu_t)); @@ -591,7 +591,7 @@ qboolean MultiBeginGame (union menuoption_s *option,struct menu_s *menu, int key Cbuf_AddText("echo You can use the setrenderer command to return to a graphical interface at any time\n", RESTRICT_LOCAL); } - M_RemoveAllMenus(); + M_RemoveAllMenus(true); return true; } @@ -656,7 +656,7 @@ void M_Menu_GameOptions_f (void) int mgt; int players; - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); m_state = m_complex; menu = M_CreateMenu(sizeof(newmultimenu_t)); diff --git a/engine/client/m_options.c b/engine/client/m_options.c index c2f3d26b4..4f122635a 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -103,7 +103,7 @@ menu_t *M_Options_Title(int *y, int infosize) struct menu_s *menu; *y = 32; - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); m_state = m_complex; menu = M_CreateMenu(infosize); @@ -365,7 +365,7 @@ void M_Menu_Audio_Speakers_f (void) audiomenuinfo_t *info; menu_t *menu; - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); m_state = m_complex; menu = M_CreateMenu(sizeof(audiomenuinfo_t)); @@ -2651,7 +2651,7 @@ static void M_BoneDisplay(entity_t *e, galiasbone_t *b, int *y, int depth, int p { float result[12]; memset(result, 0, sizeof(result)); - if (Mod_GetTag(e->model, i, &e->framestate, result)) + if (Mod_GetTag(e->model, i+1, &e->framestate, result)) Draw_FunString(depth*16, *y, va("%i: %s (%g %g %g)", i, b[i].name, result[3], result[7], result[11])); else Draw_FunString(depth*16, *y, va("%i: %s", i, b[i].name)); @@ -2882,7 +2882,7 @@ void M_Menu_ModelViewer_f(void) menucustom_t *c; menu_t *menu; - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); menu = M_CreateMenu(sizeof(*mv)); mv = menu->data; @@ -2996,7 +2996,7 @@ void M_Menu_Mods_f (void) } else { - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); menu = M_CreateMenu(sizeof(modmenu_t)); *(modmenu_t*)menu->data = mods; diff --git a/engine/client/m_script.c b/engine/client/m_script.c index 5a15622ac..5e3c41c94 100644 --- a/engine/client/m_script.c +++ b/engine/client/m_script.c @@ -79,7 +79,7 @@ void M_MenuS_Script_f (void) //create a menu. extern menu_t *currentmenu; menu_t *oldmenu; char *alias = Cmd_Argv(1); - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); m_state = m_complex; selectitem = 0; diff --git a/engine/client/m_single.c b/engine/client/m_single.c index 3bd3411f8..66bda8eb1 100644 --- a/engine/client/m_single.c +++ b/engine/client/m_single.c @@ -137,7 +137,7 @@ void M_Menu_Save_f (void) if (cl.intermission) return; - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); m_state = m_complex; menu = M_CreateMenu(sizeof(loadsavemenuinfo_t)); @@ -164,7 +164,7 @@ void M_Menu_Load_f (void) menu_t *menu; int i; - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); m_state = m_complex; menu = M_CreateMenu(sizeof(loadsavemenuinfo_t)); @@ -201,7 +201,7 @@ void M_Menu_SinglePlayer_f (void) #endif static menuresel_t resel; - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); m_state = m_complex; #ifdef CLIENTONLY @@ -565,6 +565,7 @@ static qboolean M_DemoKey(menucustom_t *control, menu_t *menu, int key, unsigned ShowDemoMenu(menu, info->selected->name); else { + extern int shift_down; int extnum; for (extnum = 0; extnum < info->numext; extnum++) if (!stricmp(info->selected->name + strlen(info->selected->name)-4, info->ext[extnum])) @@ -574,7 +575,8 @@ static qboolean M_DemoKey(menucustom_t *control, menu_t *menu, int key, unsigned extnum = 0; Cbuf_AddText(va("%s \"%s%s\"\n", info->command[extnum], (info->fs->fsroot==FS_SYSTEM)?"#":"", info->selected->name), RESTRICT_LOCAL); - M_RemoveMenu(menu); + if (!shift_down) + M_RemoveMenu(menu); return true; } } @@ -832,7 +834,7 @@ void M_Menu_Demos_f (void) menu_t *menu; static demoloc_t mediareenterloc = {FS_GAME}; - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); Key_Dest_Remove(kdm_console); m_state = m_complex; @@ -885,7 +887,7 @@ void M_Menu_MediaFiles_f (void) menu_t *menu; static demoloc_t mediareenterloc = {FS_GAME}; - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); m_state = m_complex; menu = M_CreateMenu(sizeof(demomenu_t)); @@ -893,19 +895,28 @@ void M_Menu_MediaFiles_f (void) info = menu->data; info->fs = &mediareenterloc; + info->numext = 0; - info->ext[0] = ".m3u"; - info->command[0] = "mediaplaylist"; - info->ext[1] = ".mp3"; - info->command[1] = "mediaadd"; - info->ext[2] = ".wav"; - info->command[2] = "mediaadd"; - info->ext[3] = ".ogg"; //will this ever be added properly? - info->command[3] = "mediaadd"; - info->ext[4] = ".roq"; - info->command[4] = "playfilm"; - info->numext = 5; + info->ext[info->numext] = ".m3u"; + info->command[info->numext] = "mediaplaylist"; + info->numext++; +#if defined(_WIN32) || defined(FTE_TARGET_WEB) + info->ext[info->numext] = ".mp3"; + info->command[info->numext] = "media_add"; + info->numext++; +#endif + info->ext[info->numext] = ".wav"; + info->command[info->numext] = "media_add"; + info->numext++; +#if defined(AVAIL_OGGVORBIS) || defined(FTE_TARGET_WEB) + info->ext[info->numext] = ".ogg"; //will this ever be added properly? + info->command[info->numext] = "media_add"; + info->numext++; +#endif + info->ext[info->numext] = ".roq"; + info->command[info->numext] = "playfilm"; + info->numext++; #ifdef _WIN32 //avis are only playable on windows due to a windows dll being used to decode them. info->ext[info->numext] = ".avi"; info->command[info->numext] = "playfilm"; diff --git a/engine/client/menu.c b/engine/client/menu.c index 97b8cf5f7..d9494d6dd 100644 --- a/engine/client/menu.c +++ b/engine/client/menu.c @@ -300,10 +300,10 @@ int m_save_demonum; void M_CloseMenu_f (void) { - if (!Key_Dest_Has(kdm_menu)) + if (!Key_Dest_Has(kdm_emenu)) return; - M_RemoveAllMenus(); - Key_Dest_Remove(kdm_menu); + M_RemoveAllMenus(false); + Key_Dest_Remove(kdm_emenu); m_state = m_none; } /* @@ -315,7 +315,7 @@ void M_ToggleMenu_f (void) { if (m_state) { - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); return; } @@ -339,9 +339,9 @@ void M_ToggleMenu_f (void) #endif //it IS a toggle, so close the menu if its already active - if (Key_Dest_Has(kdm_menu)) + if (Key_Dest_Has(kdm_emenu)) { - Key_Dest_Remove(kdm_menu); + Key_Dest_Remove(kdm_emenu); m_state = m_none; return; } @@ -494,7 +494,7 @@ void M_Menu_Keys_f (void) menu_t *menu; vfsfile_t *bindslist; - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); m_state = m_complex; menu = M_CreateMenu(0); @@ -618,7 +618,7 @@ struct void M_Menu_Help_f (void) { int i; - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); m_state = m_help; help_page = 0; @@ -734,7 +734,7 @@ void M_Menu_Prompt (void (*callback)(void *, int), void *ctx, char *m1, char *m2 promptmenu_t *m; char *t; - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); m_state = m_complex; m = (promptmenu_t*)M_CreateMenuInfront(sizeof(*m) - sizeof(m->m) + strlen(m1)+strlen(m2)+strlen(m3)+strlen(optionyes)+strlen(optionyes)+strlen(optioncancel)+6); @@ -1035,7 +1035,7 @@ void M_Menu_Quit_f (void) #endif break; case 2: - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); Key_Dest_Remove(kdm_console); m_state = m_complex; @@ -1059,7 +1059,7 @@ void M_Menu_Quit_f (void) MC_AddBox (quitmenu, 56, 76, 25, 5); break; case 1: - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_emenu); Key_Dest_Remove(kdm_console); m_state = m_complex; @@ -1139,10 +1139,6 @@ void M_Init_Internal (void) Cmd_AddCommand ("menu_keys", M_Menu_Keys_f); Cmd_AddCommand ("help", M_Menu_Help_f); Cmd_AddCommand ("menu_quit", M_Menu_Quit_f); -#ifndef NOMEDIAMENU - Cmd_AddCommand ("menu_media", M_Menu_Media_f); -#endif - Cmd_AddCommand ("menu_mediafiles", M_Menu_MediaFiles_f); Cmd_AddCommand ("menu_mods", M_Menu_Mods_f); Cmd_AddCommand ("modelviewer", M_Menu_ModelViewer_f); @@ -1191,7 +1187,7 @@ void M_Init_Internal (void) void M_DeInit_Internal (void) { - M_RemoveAllMenus(); + M_RemoveAllMenus(true); if (!internalmenusregistered) return; @@ -1208,10 +1204,6 @@ void M_DeInit_Internal (void) Cmd_RemoveCommand ("menu_keys"); Cmd_RemoveCommand ("help"); Cmd_RemoveCommand ("menu_quit"); -#ifndef NOMEDIAMENU - Cmd_RemoveCommand ("menu_media"); -#endif - Cmd_RemoveCommand ("menu_mediafiles"); #ifdef CL_MASTER Cmd_RemoveCommand ("menu_slist"); @@ -1286,6 +1278,7 @@ void M_Init (void) #endif //demo menu is allowed to see outside of the quakedir. you can't replicate that in qc's sandbox. Cmd_AddCommand ("menu_demo", M_Menu_Demos_f); + Cmd_AddCommand ("menu_mediafiles", M_Menu_MediaFiles_f); @@ -1341,18 +1334,18 @@ void M_Draw (int uimenu) #ifndef NOBUILTINMENUS if (m_state != m_complex) { - M_RemoveAllMenus(); + M_RemoveAllMenus(false); menu_mousedown = false; } #endif - if (!Key_Dest_Has(kdm_menu)) + if (!Key_Dest_Has(kdm_emenu)) { m_state = m_none; return; } - if (m_state == m_none || m_state == m_menu_dat) + if (m_state == m_none) return; #ifndef NOBUILTINMENUS @@ -1388,21 +1381,10 @@ void M_Draw (int uimenu) break; #endif -#ifndef NOMEDIAMENU - case m_media: - M_Media_Draw (); - break; -#endif - #ifdef PLUGINS case m_plugin: Plug_Menu_Event (0, (int)(realtime*1000)); break; -#endif -#ifdef MENU_DAT - case m_menu_dat: -// MP_Draw(); - return; #endif } } @@ -1414,7 +1396,7 @@ void M_Keydown (int key, int unicode) { default: case m_none: - Key_Dest_Remove(kdm_menu); + Key_Dest_Remove(kdm_emenu); return; #ifndef NOBUILTINMENUS case m_help: @@ -1429,21 +1411,10 @@ void M_Keydown (int key, int unicode) return; #endif -#ifndef NOMEDIAMENU - case m_media: - M_Media_Key (key); - return; -#endif - #ifdef PLUGINS case m_plugin: Plug_Menu_Event (1, key); return; -#endif -#ifdef MENU_DAT - case m_menu_dat: - MP_Keydown(key, unicode); - return; #endif } } @@ -1464,11 +1435,6 @@ void M_Keyup (int key, int unicode) case m_plugin: Plug_Menu_Event (2, key); return; -#endif -#ifdef MENU_DAT - case m_menu_dat: - MP_Keyup(key, unicode); - return; #endif default: break; diff --git a/engine/client/menu.h b/engine/client/menu.h index 1d0ad69a9..02b1410bd 100644 --- a/engine/client/menu.h +++ b/engine/client/menu.h @@ -24,12 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. //m_none - menu is disabled //m_complex - hirachy of item types //m_help - old q1 style help menu (fixme: make m_complex) -//m_keys - old q1 style key menu (fixme: make m_complex) -//m_slist - serverbrowser. Takes full control of screen. -//m_media - an mp3 player type thing. It was never really compleate. -// It should perhaps either be fixed or removed. //m_plugin - A QVM based or DLL based plugin. -//m_menu_dat- A QC based version of m_plugin. This should be compatible with DP's menu.dat stuff. //the m_complex menu state is the most advanced, and drives the bulk of FTE's menus in an event driven way. @@ -96,12 +91,10 @@ void M_SomeInitialisationFunctionCalledAtStartup(void) } */ -typedef enum {m_none, m_complex, m_help, m_media, m_plugin, m_menu_dat} m_state_t; +typedef enum {m_none, m_complex, m_help, m_plugin} m_state_t; extern m_state_t m_state; void M_DrawTextBox (int x, int y, int width, int lines); -#define NOMEDIAMENU - #ifndef NOBUILTINMENUS // @@ -380,7 +373,7 @@ void M_AddMenu (menu_t *menu); void M_AddMenuFront (menu_t *menu); void M_HideMenu (menu_t *menu); void M_RemoveMenu (menu_t *menu); -void M_RemoveAllMenus (void); +void M_RemoveAllMenus (qboolean leaveprompts); void M_Complex_Key(int key, int unicode); void M_Complex_Draw(void); @@ -404,7 +397,6 @@ void M_Menu_LanConfig_f (void); void M_Menu_GameOptions_f (void); void M_Menu_Search_f (void); void M_Menu_ServerList_f (void); -void M_Menu_Media_f (void); /* void M_Main_Draw (void); @@ -440,10 +432,6 @@ void M_Search_Key (int key); void M_ServerList_Key (int key); */ -void MasterInfo_Refresh(void); -void M_DrawServers(void); -void M_SListKey(int key); - //drawing funcs void M_BuildTranslationTable(unsigned int pc, unsigned int top, unsigned int bottom, unsigned int *translationTable); void M_DrawCharacter (int cx, int line, unsigned int num); @@ -471,9 +459,7 @@ void M_Draw (int uimenu); void M_FindKeysForCommand (int pnum, const char *command, int *twokeys); int M_FindKeysForBind (const char *command, int *keylist, int *keymods, int total); -void M_Media_Draw (void); -void M_Media_Key (int key); - +#ifdef MENU_DAT void MP_CvarChanged(cvar_t *var); qboolean MP_Init (void); void MP_Shutdown (void); @@ -483,6 +469,7 @@ void MP_RegisterCvarsAndCmds(void); void MP_Keydown(int key, int unicode); void MP_Keyup(int key, int unicode); int MP_BuiltinValid(char *name, int num); +#endif #define MGT_BAD ~0 #define MGT_QUAKE1 0 diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 09904993f..63a698ddf 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -3667,7 +3667,7 @@ static void QCBUILTIN PF_cl_te_lightning1 (pubprogfuncs_t *prinst, struct global float *start = G_VECTOR(OFS_PARM1); float *end = G_VECTOR(OFS_PARM2); - CL_AddBeam(0, ent->entnum+MAX_EDICTS, start, end); + CL_AddBeam(BT_Q1LIGHTNING1, ent->entnum+MAX_EDICTS, start, end); } static void QCBUILTIN PF_cl_te_lightning2 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -3675,7 +3675,7 @@ static void QCBUILTIN PF_cl_te_lightning2 (pubprogfuncs_t *prinst, struct global float *start = G_VECTOR(OFS_PARM1); float *end = G_VECTOR(OFS_PARM2); - CL_AddBeam(1, ent->entnum+MAX_EDICTS, start, end); + CL_AddBeam(BT_Q1LIGHTNING2, ent->entnum+MAX_EDICTS, start, end); } static void QCBUILTIN PF_cl_te_lightning3 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -3683,7 +3683,7 @@ static void QCBUILTIN PF_cl_te_lightning3 (pubprogfuncs_t *prinst, struct global float *start = G_VECTOR(OFS_PARM1); float *end = G_VECTOR(OFS_PARM2); - CL_AddBeam(2, ent->entnum+MAX_EDICTS, start, end); + CL_AddBeam(BT_Q1LIGHTNING3, ent->entnum+MAX_EDICTS, start, end); } static void QCBUILTIN PF_cl_te_beam (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -3691,7 +3691,7 @@ static void QCBUILTIN PF_cl_te_beam (pubprogfuncs_t *prinst, struct globalvars_s float *start = G_VECTOR(OFS_PARM1); float *end = G_VECTOR(OFS_PARM2); - CL_AddBeam(5, ent->entnum+MAX_EDICTS, start, end); + CL_AddBeam(BT_Q1BEAM, ent->entnum+MAX_EDICTS, start, end); } static void QCBUILTIN PF_cl_te_plasmaburn (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index 5a2028e9e..40e9d4c0d 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -10,6 +10,8 @@ #if defined(MENU_DAT) || defined(CSQC_DAT) #include "cl_master.h" +qbyte mpkeysdown[K_MAX/8]; + extern unsigned int r2d_be_flags; #define DRAWFLAG_NORMAL 0 #define DRAWFLAG_ADD 1 @@ -391,6 +393,7 @@ void QCBUILTIN PF_CL_drawcolouredstring (pubprogfuncs_t *prinst, struct globalva float flag = 0; float r, g, b; float px, py, ipx; + unsigned int codeflags, codepoint; conchar_t buffer[2048], *str; @@ -426,13 +429,13 @@ void QCBUILTIN PF_CL_drawcolouredstring (pubprogfuncs_t *prinst, struct globalva Font_ForceColour(r, g, b, alpha); while(*str) { - if ((*str & CON_CHARMASK) == '\n') + str = Font_Decode(str, &codeflags, &codepoint); + if (codepoint == '\n') py += Font_CharHeight(); - else if ((*str & CON_CHARMASK) == '\r') + else if (codepoint == '\r') px = ipx; else - px = Font_DrawScaleChar(px, py, *str); - str++; + px = Font_DrawScaleChar(px, py, codeflags, codepoint); } Font_InvalidateColour(); Font_EndString(NULL); @@ -682,7 +685,7 @@ void QCBUILTIN PF_CL_drawcharacter (pubprogfuncs_t *prinst, struct globalvars_s r2d_be_flags = PF_SelectDPDrawFlag(flag); PR_CL_BeginString(prinst, pos[0], pos[1], size[0], size[1], &x, &y); Font_ForceColour(rgb[0], rgb[1], rgb[2], alpha); - Font_DrawScaleChar(x, y, CON_WHITEMASK | chara); + Font_DrawScaleChar(x, y, CON_WHITEMASK, chara); Font_InvalidateColour(); Font_EndString(NULL); r2d_be_flags = 0; @@ -727,7 +730,7 @@ void QCBUILTIN PF_CL_drawrawstring (pubprogfuncs_t *prinst, struct globalvars_s else if (c & 0x80) c |= 0xe000; //if its a high char, just use the quake range instead. we could colour it, but why bother } - x = Font_DrawScaleChar(x, y, CON_WHITEMASK|c); + x = Font_DrawScaleChar(x, y, CON_WHITEMASK, c); } Font_InvalidateColour(); Font_EndString(NULL); @@ -1236,25 +1239,25 @@ void QCBUILTIN PF_CL_precache_sound (pubprogfuncs_t *prinst, struct globalvars_s //void setkeydest(float dest) = #601; void QCBUILTIN PF_cl_setkeydest (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { + //these arguments are stupid switch((int)G_FLOAT(OFS_PARM0)) { case 0: // key_game - if (Key_Dest_Has(kdm_menu)) + if (Key_Dest_Has(kdm_gmenu)) { - Key_Dest_Remove(kdm_menu); + Key_Dest_Remove(kdm_gmenu); // Key_Dest_Remove(kdm_message); - if (cls.state == ca_disconnected) - Key_Dest_Add(kdm_console); +// if (cls.state == ca_disconnected) +// Key_Dest_Add(kdm_console); } break; case 2: // key_menu - m_state = m_menu_dat; Key_Dest_Remove(kdm_message); - if (!Key_Dest_Has(kdm_menu)) + if (!Key_Dest_Has(kdm_gmenu)) Key_Dest_Remove(kdm_console); - Key_Dest_Add(kdm_menu); + Key_Dest_Add(kdm_gmenu); break; case 1: // key_message @@ -1268,13 +1271,10 @@ void QCBUILTIN PF_cl_setkeydest (pubprogfuncs_t *prinst, struct globalvars_s *pr //float getkeydest(void) = #602; void QCBUILTIN PF_cl_getkeydest (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - if (Key_Dest_Has(kdm_menu)) - { - if (m_state == m_menu_dat) - G_FLOAT(OFS_RETURN) = 2; - else - G_FLOAT(OFS_RETURN) = 3; - } + if (Key_Dest_Has(kdm_emenu)) + G_FLOAT(OFS_RETURN) = 3; + else if (Key_Dest_Has(kdm_gmenu)) + G_FLOAT(OFS_RETURN) = 2; // else if (Key_Dest_Has(kdm_message)) // G_FLOAT(OFS_RETURN) = 1; else @@ -2241,19 +2241,14 @@ void MP_Shutdown (void) PR_Common_Shutdown(menu_world.progs, false); menu_world.progs->CloseProgs(menu_world.progs); memset(&menu_world, 0, sizeof(menu_world)); - PR_ReleaseFonts(kdm_menu); + PR_ReleaseFonts(kdm_gmenu); #ifdef CL_MASTER Master_ClearMasks(); #endif - if (m_state == m_menu_dat) - { - Key_Dest_Remove(kdm_menu); - m_state = 0; - } - - key_dest_absolutemouse &= ~kdm_menu; + Key_Dest_Remove(kdm_gmenu); + key_dest_absolutemouse &= ~kdm_gmenu; } void *VARGS PR_CB_Malloc(int size); //these functions should be tracked by the library reliably, so there should be no need to track them ourselves. @@ -2358,7 +2353,7 @@ qboolean MP_Init (void) menuprogparms.useeditor = QCEditor;//void (*useeditor) (char *filename, int line, int nump, char **parms); menuprogparms.user = &menu_world; - menu_world.keydestmask = kdm_menu; + menu_world.keydestmask = kdm_gmenu; menutime = Sys_DoubleTime(); if (!menu_world.progs) @@ -2537,6 +2532,7 @@ void MP_Draw(void) inmenuprogs--; } + void MP_Keydown(int key, int unicode) { extern qboolean keydown[K_MAX]; @@ -2559,6 +2555,8 @@ void MP_Keydown(int key, int unicode) } } + mpkeysdown[key>>3] |= (1<<(key&7)); + menutime = Sys_DoubleTime(); if (menu_world.g.time) *menu_world.g.time = menutime; @@ -2584,6 +2582,10 @@ void MP_Keyup(int key, int unicode) if (setjmp(mp_abort)) return; + if (key && !(mpkeysdown[key>>3] & (1<<(key&7)))) + return; + mpkeysdown[key>>3] &= ~(1<<(key&7)); + menutime = Sys_DoubleTime(); if (menu_world.g.time) *menu_world.g.time = menutime; diff --git a/engine/client/quakedef.h b/engine/client/quakedef.h index 5b3d65de0..e25642b6e 100644 --- a/engine/client/quakedef.h +++ b/engine/client/quakedef.h @@ -157,6 +157,7 @@ extern "C" { #include "protocol.h" #include "cmd.h" #include "wad.h" +#include "console.h" #include "screen.h" #include "sbar.h" #include "sound.h" @@ -169,7 +170,6 @@ extern "C" { #include "input.h" #include "keys.h" -#include "console.h" #include "view.h" #include "menu.h" #include "crc.h" diff --git a/engine/client/r_2d.c b/engine/client/r_2d.c index 0f4ac82dd..d81ad85b7 100644 --- a/engine/client/r_2d.c +++ b/engine/client/r_2d.c @@ -1315,10 +1315,10 @@ void R2D_DrawCrosshair(void) { SCR_CrosshairPosition(&cl.playerview[sc], &sx, &sy); Font_BeginScaledString(font_default, sx, sy, size, size, &sx, &sy); - sx -= Font_CharScaleWidth('+' | 0xe000 | CON_WHITEMASK)/2; + sx -= Font_CharScaleWidth(CON_WHITEMASK, '+' | 0xe000)/2; sy -= Font_CharScaleHeight()/2; Font_ForceColour(ch_color[0], ch_color[1], ch_color[2], crosshairalpha.value); - Font_DrawScaleChar(sx, sy, '+' | 0xe000 | CON_WHITEMASK); + Font_DrawScaleChar(sx, sy, CON_WHITEMASK, '+' | 0xe000); Font_InvalidateColour(); Font_EndString(font_default); } diff --git a/engine/client/render.h b/engine/client/render.h index 7eedb5a59..f7b97864a 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -582,6 +582,7 @@ extern cvar_t gl_maxdist; extern cvar_t r_clear; extern cvar_t gl_poly; extern cvar_t gl_affinemodels; +extern cvar_t r_renderscale; extern cvar_t gl_nohwblend; extern cvar_t r_coronas, r_flashblend, r_flashblendscale; extern cvar_t r_lightstylesmooth; diff --git a/engine/client/renderer.c b/engine/client/renderer.c index f6c1e7695..b835ef299 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -142,6 +142,7 @@ cvar_t r_stainfadetime = SCVAR ("r_stainfadetime", "1"); cvar_t r_stains = CVARFC("r_stains", IFMINIMAL("0","0.75"), CVAR_ARCHIVE, Cvar_Limiter_ZeroToOne_Callback); +cvar_t r_fxaa = CVARD("r_fxaa", "0", "Runs a post-procesing pass to strip the jaggies."); cvar_t r_postprocshader = CVARD("r_postprocshader", "", "Specifies a custom shader to use as a post-processing shader"); cvar_t r_wallcolour = CVARAF ("r_wallcolour", "128 128 128", "r_wallcolor", CVAR_RENDERERCALLBACK|CVAR_SHADERSYSTEM);//FIXME: broken @@ -430,6 +431,8 @@ void GLRenderer_Init(void) Cvar_Register (&gl_lateswap, GLRENDEREROPTIONS); Cvar_Register (&gl_lerpimages, GLRENDEREROPTIONS); Cvar_Register (&r_postprocshader, GLRENDEREROPTIONS); + Cvar_Register (&r_fxaa, GLRENDEREROPTIONS); + Cvar_Register (&r_renderscale, GLRENDEREROPTIONS); Cvar_Register (&dpcompat_psa_ungroup, GLRENDEREROPTIONS); Cvar_Register (&r_lerpmuzzlehack, GLRENDEREROPTIONS); diff --git a/engine/client/sbar.c b/engine/client/sbar.c index 30bad9eeb..f02081e78 100644 --- a/engine/client/sbar.c +++ b/engine/client/sbar.c @@ -176,10 +176,12 @@ int Sbar_BottomColour(player_info_t *p) void Draw_ExpandedString(float x, float y, conchar_t *str) { int px, py; + unsigned int codeflags, codepoint; Font_BeginString(font_default, x, y, &px, &py); while(*str) { - px = Font_DrawChar(px, py, *str++); + str = Font_Decode(str, &codeflags, &codepoint); + px = Font_DrawChar(px, py, codeflags, codepoint); } Font_EndString(font_default); } @@ -208,6 +210,7 @@ void Draw_FunStringWidth(float x, float y, const void *str, int width, qboolean conchar_t *w; int px, py; int fw = 0; + unsigned int codeflags, codepoint; width = (width*vid.rotpixelwidth)/vid.width; @@ -216,9 +219,10 @@ void Draw_FunStringWidth(float x, float y, const void *str, int width, qboolean Font_BeginString(font_default, x, y, &px, &py); if (rightalign) { - for (w = buffer; *w; w++) + for (w = buffer; *w; ) { - fw += Font_CharWidth(*w); + w = Font_Decode(w, &codeflags, &codepoint); + fw += Font_CharWidth(codeflags, codepoint); } if (rightalign == 2) { @@ -237,12 +241,14 @@ void Draw_FunStringWidth(float x, float y, const void *str, int width, qboolean } } - for (w = buffer; *w; w++) + for (w = buffer; *w; ) { - width -= Font_CharWidth(*w); + w = Font_Decode(w, &codeflags, &codepoint); + + width -= Font_CharWidth(codeflags, codepoint); if (width < 0) return; - px = Font_DrawChar(px, py, *w); + px = Font_DrawChar(px, py, codeflags, codepoint); } Font_EndString(font_default); } @@ -1115,7 +1121,7 @@ void Sbar_DrawCharacter (float x, float y, int num) { int px, py; Font_BeginString(font_default, sbar_rect.x + x + 4, sbar_rect.y + y + sbar_rect.height-SBAR_HEIGHT, &px, &py); - Font_DrawChar(px, py, num | 0xe000 | CON_WHITEMASK); + Font_DrawChar(px, py, CON_WHITEMASK, num | 0xe000); Font_EndString(font_default); } @@ -1158,7 +1164,8 @@ void Draw_TinyString (float x, float y, const qbyte *str) str++; continue; } - px = Font_DrawChar(px, py, CON_WHITEMASK|*str++); + //fixme: utf-8 encoding. + px = Font_DrawChar(px, py, CON_WHITEMASK, *str++); } Font_EndString(font_tiny); } @@ -2593,24 +2600,24 @@ static void Sbar_Voice(int y) float range = loudness/100.0f; w = 0; Font_BeginString(font_default, sbar_rect.x + sbar_rect.width/2, sbar_rect.y + y + sbar_rect.height-SBAR_HEIGHT, &x, &y); - w += Font_CharWidth(0xe080 | CON_WHITEMASK); - w += Font_CharWidth(0xe081 | CON_WHITEMASK)*16; - w += Font_CharWidth(0xe082 | CON_WHITEMASK); - w += Font_CharWidth('M' | CON_WHITEMASK); - w += Font_CharWidth('i' | CON_WHITEMASK); - w += Font_CharWidth('c' | CON_WHITEMASK); - w += Font_CharWidth(' ' | CON_WHITEMASK); + w += Font_CharWidth(CON_WHITEMASK, 0xe080); + w += Font_CharWidth(CON_WHITEMASK, 0xe081)*16; + w += Font_CharWidth(CON_WHITEMASK, 0xe082); + w += Font_CharWidth(CON_WHITEMASK, 'M'); + w += Font_CharWidth(CON_WHITEMASK, 'i'); + w += Font_CharWidth(CON_WHITEMASK, 'c'); + w += Font_CharWidth(CON_WHITEMASK, ' '); x -= w/2; - x = Font_DrawChar(x, y, 'M' | CON_WHITEMASK); - x = Font_DrawChar(x, y, 'i' | CON_WHITEMASK); - x = Font_DrawChar(x, y, 'c' | CON_WHITEMASK); - x = Font_DrawChar(x, y, ' ' | CON_WHITEMASK); - x = Font_DrawChar(x, y, 0xe080 | CON_WHITEMASK); + x = Font_DrawChar(x, y, CON_WHITEMASK, 'M'); + x = Font_DrawChar(x, y, CON_WHITEMASK, 'i'); + x = Font_DrawChar(x, y, CON_WHITEMASK, 'c'); + x = Font_DrawChar(x, y, CON_WHITEMASK, ' '); + x = Font_DrawChar(x, y, CON_WHITEMASK, 0xe080); s = x; for (i=0 ; i<16 ; i++) - x = Font_DrawChar(x, y, 0xe081 | CON_WHITEMASK); - Font_DrawChar(x, y, 0xe082 | CON_WHITEMASK); - Font_DrawChar(s + (x-s) * range - Font_CharWidth(0xe083 | CON_WHITEMASK)/2, y, 0xe083 | CON_WHITEMASK); + x = Font_DrawChar(x, y, CON_WHITEMASK, 0xe081); + Font_DrawChar(x, y, CON_WHITEMASK, 0xe082); + Font_DrawChar(s + (x-s) * range - Font_CharWidth(CON_WHITEMASK, 0xe083)/2, y, CON_WHITEMASK, 0xe083); Font_EndString(font_default); } #endif @@ -3137,19 +3144,19 @@ ping time frags name sprintf(num, "%3i",f); \ \ Font_BeginString(font_default, x+8, y, &cx, &cy); \ - Font_DrawChar(cx, cy, num[0] | 0xe000 | CON_WHITEMASK); \ + Font_DrawChar(cx, cy, CON_WHITEMASK, num[0] | 0xe000); \ Font_BeginString(font_default, x+16, y, &cx, &cy); \ - Font_DrawChar(cx, cy, num[1] | 0xe000 | CON_WHITEMASK); \ + Font_DrawChar(cx, cy, CON_WHITEMASK, num[1] | 0xe000); \ Font_BeginString(font_default, x+24, y, &cx, &cy); \ - Font_DrawChar(cx, cy, num[2] | 0xe000 | CON_WHITEMASK); \ + Font_DrawChar(cx, cy, CON_WHITEMASK, num[2] | 0xe000); \ \ if ((pv->cam_state == CAM_FREECAM && k == pv->playernum) || \ (pv->cam_state != CAM_FREECAM && k == pv->cam_spec_track)) \ { \ Font_BeginString(font_default, x, y, &cx, &cy); \ - Font_DrawChar(cx, cy, 16 | 0xe000 | CON_WHITEMASK); \ + Font_DrawChar(cx, cy, CON_WHITEMASK, 16 | 0xe000); \ Font_BeginString(font_default, x+32, y, &cx, &cy); \ - Font_DrawChar(cx, cy, 17 | 0xe000 | CON_WHITEMASK); \ + Font_DrawChar(cx, cy, CON_WHITEMASK, 17 | 0xe000); \ } \ Font_EndString(font_default); \ } \ @@ -3588,19 +3595,19 @@ static void Sbar_MiniDeathmatchOverlay (playerview_t *pv) sprintf (num, "%3i",f); Font_BeginString(font_default, x+8, y, &px, &py); - Font_DrawChar ( px, py, num[0] | 0xe000 | CON_WHITEMASK); + Font_DrawChar ( px, py, CON_WHITEMASK, num[0] | 0xe000); Font_BeginString(font_default, x+16, y, &px, &py); - Font_DrawChar ( px, py, num[1] | 0xe000 | CON_WHITEMASK); + Font_DrawChar ( px, py, CON_WHITEMASK, num[1] | 0xe000); Font_BeginString(font_default, x+24, y, &px, &py); - Font_DrawChar ( px, py, num[2] | 0xe000 | CON_WHITEMASK); + Font_DrawChar ( px, py, CON_WHITEMASK, num[2] | 0xe000); if ((cl.spectator && k == pv->cam_spec_track && pv->cam_state != CAM_FREECAM) || (!cl.spectator && k == pv->playernum)) { Font_BeginString(font_default, x, y, &px, &py); - Font_DrawChar ( px, py, 16 | 0xe000 | CON_WHITEMASK); + Font_DrawChar ( px, py, CON_WHITEMASK, 16 | 0xe000); Font_BeginString(font_default, x+32, y, &px, &py); - Font_DrawChar ( px, py, 17 | 0xe000 | CON_WHITEMASK); + Font_DrawChar ( px, py, CON_WHITEMASK, 17 | 0xe000); } Q_strncpyz(name, s->name, sizeof(name)); @@ -3642,9 +3649,9 @@ static void Sbar_MiniDeathmatchOverlay (playerview_t *pv) if (!strncmp(cl.players[pv->playernum].team, tm->team, 16)) { Font_BeginString(font_default, x-8, y, &px, &py); - Font_DrawChar(px, py, 16|0xe000|CON_WHITEMASK); + Font_DrawChar(px, py, CON_WHITEMASK, 16|0xe000); Font_BeginString(font_default, x+32, y, &px, &py); - Font_DrawChar(px, py, 17|0xe000|CON_WHITEMASK); + Font_DrawChar(px, py, CON_WHITEMASK, 17|0xe000); Font_EndString(font_default); } diff --git a/engine/client/screen.h b/engine/client/screen.h index 4bf7f6e30..505c1ada9 100644 --- a/engine/client/screen.h +++ b/engine/client/screen.h @@ -108,15 +108,30 @@ void Font_BeginScaledString(struct font_s *font, float vx, float vy, float szx, void Font_Transform(float vx, float vy, int *px, int *py); int Font_CharHeight(void); float Font_CharScaleHeight(void); -int Font_CharWidth(unsigned int charcode); -float Font_CharScaleWidth(unsigned int charcode); -int Font_CharEndCoord(struct font_s *font, int x, unsigned int charcode); -int Font_DrawChar(int px, int py, unsigned int charcode); -float Font_DrawScaleChar(float px, float py, unsigned int charcode); /*avoid using*/ +int Font_CharWidth(unsigned int charflags, unsigned int codepoint); +float Font_CharScaleWidth(unsigned int charflags, unsigned int codepoint); +int Font_CharEndCoord(struct font_s *font, int x, unsigned int charflags, unsigned int codepoint); +int Font_DrawChar(int px, int py, unsigned int charflags, unsigned int codepoint); +float Font_DrawScaleChar(float px, float py, unsigned int charflags, unsigned int codepoint); /*avoid using*/ void Font_EndString(struct font_s *font); void Font_ForceColour(float r, float g, float b, float a); //This colour will be applied while the char mask remains WHITE. If you print char by char, make sure to include the mask. void Font_InvalidateColour(void); /*these three functions deal with formatted blocks of text (including tabs and new lines)*/ +fte_inline conchar_t *Font_Decode(conchar_t *start, unsigned int *codeflags, unsigned int *codepoint) +{ + if (*start & CON_LONGCHAR) + if (!(*start & CON_RICHFORECOLOUR)) + { + *codeflags = start[1]; + *codepoint = ((start[0] & CON_CHARMASK)<<16) | (start[1] & CON_CHARMASK); + return start+2; + } + + *codeflags = start[0] & CON_FLAGSMASK; + *codepoint = start[0] & CON_CHARMASK; + return start+1; +} +conchar_t *Font_DecodeReverse(conchar_t *start, conchar_t *stop, unsigned int *codeflags, unsigned int *codepoint); int Font_LineBreaks(conchar_t *start, conchar_t *end, int maxpixelwidth, int maxlines, conchar_t **starts, conchar_t **ends); int Font_LineWidth(conchar_t *start, conchar_t *end); float Font_LineScaleWidth(conchar_t *start, conchar_t *end); diff --git a/engine/client/snd_dma.c b/engine/client/snd_dma.c index 09f0b446a..cf3a06fa1 100644 --- a/engine/client/snd_dma.c +++ b/engine/client/snd_dma.c @@ -2098,6 +2098,9 @@ void S_Purge(qboolean retaintouched) if (retaintouched && sfx->touched) continue; + if (S_IsPlayingSomewhere(sfx)) + continue; //eep?!? + sfx->loadstate = SLS_NOTLOADED; /*nothing to do if there's no data within*/ if (!sfx->decoder.buf) @@ -2380,6 +2383,48 @@ void S_StartSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float f S_UnlockMixer(); } +qboolean S_GetMusicInfo(int musicchannel, float *time, float *duration) +{ + qboolean result = false; + soundcardinfo_t *sc; + sfxcache_t scachebuf, *c; + sfx_t *sfx; + *time = 0; + *duration = 0; + + musicchannel += MUSIC_FIRST; + + S_LockMixer(); + for (sc = sndcardinfo; sc; sc = sc->next) + { + sfx = sc->channel[musicchannel].sfx; + if (sfx) + { + if (sfx->loadstate != SLS_LOADED) + c = NULL; + else if (sfx->decoder.decodedata) + { + if (sfx->decoder.querydata) + c = sfx->decoder.querydata(sfx, &scachebuf); + else + c = NULL; //don't bother trying to actually decode anything here. + } + else + c = sfx->decoder.buf; + + if (c) + { + *duration = c->length / c->speed; + *time = (sc->channel[musicchannel].pos>>PITCHSHIFT) / (float)snd_speed; //the time into the sound, ignoring play rate. + result = true; + } + } + } + S_UnlockMixer(); + + return result; +} + float S_GetSoundTime(int entnum, int entchannel) { int i; @@ -2448,6 +2493,7 @@ void S_StopAllSounds(qboolean clear) { int i; sfx_t *s; + channel_t musics[NUM_MUSICS]; soundcardinfo_t *sc; @@ -2458,12 +2504,15 @@ void S_StopAllSounds(qboolean clear) for (sc = sndcardinfo; sc; sc = sc->next) { for (i=0 ; itotal_chans ; i++) + { + if (i >= MUSIC_FIRST && i < MUSIC_FIRST+NUM_MUSICS && sc->selfpainting) + continue; //don't reset music if is safe to continue playing it without stuttering if (sc->channel[i].sfx) { s = sc->channel[i].sfx; if (s->loadstate == SLS_LOADING) - ;//COM_WorkerPartialSync(s, &s->loadstate, SLS_LOADING); - else + COM_WorkerPartialSync(s, &s->loadstate, SLS_LOADING); +// else { sc->channel[i].sfx = NULL; if (s->decoder.ended) @@ -2475,12 +2524,15 @@ void S_StopAllSounds(qboolean clear) sc->ChannelUpdate(sc, &sc->channel[i], true); } } + } sc->total_chans = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS + NUM_MUSICS; // no statics + memcpy(musics, &sc->channel[MUSIC_FIRST], sizeof(musics)); Q_memset(sc->channel, 0, MAX_CHANNELS * sizeof(channel_t)); + memcpy(&sc->channel[MUSIC_FIRST], musics, sizeof(musics)); - if (clear) + if (clear && !sc->selfpainting) //if its self-painting, then the mixer will continue painting anyway (which is important if its still painting music, but otherwise don't stutter at all when loading) S_ClearBuffer (sc); } @@ -2613,12 +2665,24 @@ void S_Music_Seek(float time) } } +//mixer must be locked +qboolean S_Music_Playing(int musicchannel) +{ + soundcardinfo_t *sc; + musicchannel += MUSIC_FIRST; + for (sc = sndcardinfo; sc; sc=sc->next) + { + if (sc->channel[musicchannel].sfx) + return true; + } + return false; +} + /* =================== S_UpdateAmbientSounds =================== */ -char *Media_NextTrack(int musicchannelnum); mleaf_t *Q1BSP_LeafForPoint (model_t *model, vec3_t p); void S_UpdateAmbientSounds (soundcardinfo_t *sc) { @@ -2656,7 +2720,27 @@ void S_UpdateAmbientSounds (soundcardinfo_t *sc) if (chan->sfx) { chan->flags = CF_ABSVOLUME; //bypasses volume cvar completely. - chan->master_vol = bound(0, 255*bgmvolume.value*voicevolumemod, 255); + vol = 255*bgmvolume.value*voicevolumemod; + vol = bound(0, vol, 255); + vol = Media_CrossFade(i-MUSIC_FIRST, vol); + if (vol < 0) + { //cross fading wants to KILL this track now, apparently. + sfx_t *s = chan->sfx; + + if (s->loadstate != SLS_LOADING) + { + chan->pos = 0; + chan->sfx = NULL; + + if (sc->ChannelUpdate) + sc->ChannelUpdate(sc, chan, true); + + if (s && s->decoder.ended && !S_IsPlayingSomewhere(s)) //if we aint playing it elsewhere, free it compleatly. + s->decoder.ended(s); + } + continue; + } + chan->master_vol = bound(0, vol, 255); chan->vol[0] = chan->vol[1] = chan->vol[2] = chan->vol[3] = chan->vol[4] = chan->vol[5] = chan->master_vol; if (sc->ChannelUpdate) sc->ChannelUpdate(sc, chan, changed); @@ -3059,9 +3143,14 @@ void S_SoundList_f(void) total = 0; for (sfx=known_sfx, i=0 ; idecoder.decodedata) + if (sfx->loadstate != SLS_LOADED) + sc = NULL; + else if (sfx->decoder.decodedata) { - sc = sfx->decoder.decodedata(sfx, &scachebuf, 0, 0x0fffffff); + if (sfx->decoder.querydata) + sc = sfx->decoder.querydata(sfx, &scachebuf); + else + sc = NULL; //don't bother trying to actually decode anything here. if (!sc) { Con_Printf("S( ) : %s\n", sfx->name); diff --git a/engine/client/snd_mem.c b/engine/client/snd_mem.c index b45f22a19..0e80e5b8c 100644 --- a/engine/client/snd_mem.c +++ b/engine/client/snd_mem.c @@ -779,8 +779,10 @@ qboolean S_RegisterSoundInputPlugin(S_LoadSound_t loadfnc) return false; } -void S_Wakeup (void *ctx, void *ctxdata, size_t a, size_t b) +void S_LoadedOrFailed (void *ctx, void *ctxdata, size_t a, size_t b) { + sfx_t *s = ctx; + s->loadstate = a; } /* ============== @@ -829,7 +831,7 @@ void S_LoadSoundWorker (void *ctx, void *ctxdata, size_t a, size_t b) else { Con_SafePrintf ("Couldn't load %s\n", namebuffer); - s->loadstate = SLS_FAILED; + COM_AddWork(0, S_LoadedOrFailed, s, NULL, SLS_FAILED, 0); return; } } @@ -878,7 +880,7 @@ void S_LoadSoundWorker (void *ctx, void *ctxdata, size_t a, size_t b) { //FIXME: check to see if queued for download. Con_DPrintf ("Couldn't load %s\n", namebuffer); - s->loadstate = SLS_FAILED; + COM_AddWork(0, S_LoadedOrFailed, s, NULL, SLS_FAILED, 0); return; } @@ -890,7 +892,7 @@ void S_LoadSoundWorker (void *ctx, void *ctxdata, size_t a, size_t b) { s->loadstate = SLS_LOADED; //wake up the main thread in case it decided to wait for us. - COM_AddWork(0, S_Wakeup, s, NULL, 0, 0); + COM_AddWork(0, S_LoadedOrFailed, s, NULL, SLS_LOADED, 0); BZ_Free(data); return; } @@ -900,7 +902,7 @@ void S_LoadSoundWorker (void *ctx, void *ctxdata, size_t a, size_t b) if (s->loadstate != SLS_FAILED) Con_Printf ("Format not recognised: %s\n", namebuffer); - s->loadstate = SLS_FAILED; + COM_AddWork(0, S_LoadedOrFailed, s, NULL, SLS_FAILED, 0); BZ_Free(data); return; } diff --git a/engine/client/snd_ov.c b/engine/client/snd_ov.c index 81a786627..d17c3d4cb 100644 --- a/engine/client/snd_ov.c +++ b/engine/client/snd_ov.c @@ -75,9 +75,10 @@ typedef struct { sfx_t *s; } ovdecoderbuffer_t; -sfxcache_t *OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, int start, int length); -void OV_CancelDecoder(sfx_t *s); -qboolean OV_StartDecode(unsigned char *start, unsigned long length, ovdecoderbuffer_t *buffer); +static sfxcache_t *OV_Query(struct sfx_s *sfx, struct sfxcache_s *buf); +static sfxcache_t *OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, int start, int length); +static void OV_CancelDecoder(sfx_t *s); +static qboolean OV_StartDecode(unsigned char *start, unsigned long length, ovdecoderbuffer_t *buffer); qboolean S_LoadOVSound (sfx_t *s, qbyte *data, int datalen, int sndspeed) { @@ -93,6 +94,7 @@ qboolean S_LoadOVSound (sfx_t *s, qbyte *data, int datalen, int sndspeed) buffer->s = s; s->decoder.buf = buffer; s->decoder.decodedata = OV_DecodeSome; + s->decoder.querydata = OV_Query; s->decoder.purge = OV_CancelDecoder; s->decoder.ended = OV_CancelDecoder; @@ -117,7 +119,25 @@ qboolean S_LoadOVSound (sfx_t *s, qbyte *data, int datalen, int sndspeed) return true; } -sfxcache_t *OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, int start, int length) +static sfxcache_t *OV_Query(struct sfx_s *sfx, struct sfxcache_s *buf) +{ + ovdecoderbuffer_t *dec = sfx->decoder.buf; + if (!dec) + buf = NULL; + if (buf) + { + buf->data = NULL; //you're not meant to actually be using the data here + buf->soundoffset = 0; + buf->length = p_ov_pcm_total(&dec->vf, -1); + buf->loopstart = -1; + buf->numchannels = dec->srcchannels; + buf->speed = dec->srcspeed; + buf->width = 2; + } + return buf; +} + +static sfxcache_t *OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, int start, int length) { extern int snd_speed; extern cvar_t snd_linearresample_stream; @@ -141,9 +161,16 @@ sfxcache_t *OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, int start, dec->decodedbytestart = start; //check pos + //fixme: seeking might not be supported p_ov_pcm_seek(&dec->vf, dec->decodedbytestart); } +/* if (start > dec->decodedbytestart + dec->decodedbytecount) + { + dec->decodedbytestart = start; + p_ov_pcm_seek(&dec->vf, dec->decodedbytestart); + } +*/ if (dec->decodedbytecount > snd_speed*8) { /*everything is okay, but our buffer is getting needlessly large. @@ -243,14 +270,22 @@ sfxcache_t *OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, int start, } return buf; } -void OV_CancelDecoder(sfx_t *s) +static void OV_CanceledDecoder(void *ctx, void *data, size_t a, size_t b) +{ + sfx_t *s = ctx; + if (s->loadstate != SLS_LOADING) + s->loadstate = SLS_NOTLOADED; +} +static void OV_CancelDecoder(sfx_t *s) { ovdecoderbuffer_t *dec; + s->loadstate = SLS_FAILED; dec = s->decoder.buf; s->decoder.buf = NULL; s->decoder.purge = NULL; s->decoder.ended = NULL; + s->decoder.querydata = NULL; s->decoder.decodedata = NULL; p_ov_clear (&dec->vf); //close the decoder @@ -264,6 +299,11 @@ void OV_CancelDecoder(sfx_t *s) dec->decodedbuffer = NULL; BZ_Free(dec); + + //due to the nature of message passing, we can get into a state where the main thread is going to flag us as loaded when we have already failed. + //that is bad. + //so post a message to the main thread to override it, just in case. +// COM_AddWork(0, OV_CanceledDecoder, s, NULL, SLS_NOTLOADED, 0); s->loadstate = SLS_NOTLOADED; } @@ -316,7 +356,7 @@ static ov_callbacks callbacks = { close_func, tell_func, }; -qboolean OV_StartDecode(unsigned char *start, unsigned long length, ovdecoderbuffer_t *buffer) +static qboolean OV_StartDecode(unsigned char *start, unsigned long length, ovdecoderbuffer_t *buffer) { #ifndef LIBVORBISFILE_STATIC static qboolean tried; diff --git a/engine/client/sound.h b/engine/client/sound.h index fc22acc33..7c179f82a 100644 --- a/engine/client/sound.h +++ b/engine/client/sound.h @@ -40,6 +40,7 @@ typedef struct typedef struct { struct sfxcache_s *(*decodedata) (struct sfx_s *sfx, struct sfxcache_s *buf, int start, int length); //retrurn true when done. + struct sfxcache_s *(*querydata) (struct sfx_s *sfx, struct sfxcache_s *buf); //reports length + format info without actually decoding anything. void (*ended) (struct sfx_s *sfx); //sound stopped playing and is now silent (allow rewinding or something). void (*purge) (struct sfx_s *sfx); //sound is being purged from memory. destroy everything. void *buf; @@ -141,6 +142,10 @@ qboolean S_HaveOutput(void); void S_Music_Clear(sfx_t *onlyifsample); void S_Music_Seek(float time); +qboolean S_GetMusicInfo(int musicchannel, float *time, float *duration); +qboolean S_Music_Playing(int musicchannel); +float Media_CrossFade(int musicchanel, float vol); //queries the volume we're meant to be playing (checks for fade out). -1 for no more, otherwise returns vol. +char *Media_NextTrack(int musicchanel); //queries the track we're meant to be playing now. sfx_t *S_FindName (const char *name, qboolean create); sfx_t *S_PrecacheSound (const char *sample); diff --git a/engine/client/sys_win.c b/engine/client/sys_win.c index 28831891a..3648cc62f 100644 --- a/engine/client/sys_win.c +++ b/engine/client/sys_win.c @@ -1540,11 +1540,13 @@ void VARGS Sys_Printf (char *fmt, ...) out = wide; in = msg; wlen = 0; - for (in = msg; in < end; in++) + for (in = msg; in < end; ) { - if (!(*in & CON_HIDDEN)) + unsigned int flags, cp; + in = Font_Decode(in, &flags, &cp); + if (!(flags & CON_HIDDEN)) { - *out++ = COM_DeQuake(*in & CON_CHARMASK); + *out++ = COM_DeQuake(cp); wlen++; } } @@ -1686,9 +1688,21 @@ char *Sys_GetClipboard(void) utf8 = cliputf8 = malloc(l); while(*clipWText) { - if (clipWText[0] == '\r' && clipWText[1] == '\n') //bloomin microsoft. - clipWText++; - c = utf8_encode(utf8, *clipWText++, l); + unsigned int cp = *clipWText++; + if (cp == '\r' && *clipWText == '\n') //bloomin microsoft. + cp = *clipWText++; + if (cp >= 0xD800u && cp <= 0xDBFFu) + { //handle utf-16 surrogates + if (*clipWText >= 0xDC00u && *clipWText <= 0xDFFFu) + { + cp = (cp&0x3ff)<<10; + cp |= *clipWText++ & 0x3ff; + } + else + cp = 0xFFFDu; + } + + c = utf8_encode(utf8, cp, l); if (!c) break; l -= c; @@ -1756,13 +1770,14 @@ void Sys_SaveClipboard(char *text) HANDLE glob; char *temp; unsigned short *tempw; + unsigned int codepoint; if (!OpenClipboard(NULL)) return; - EmptyClipboard(); + EmptyClipboard(); if (com_parseutf8.ival > 0) { - glob = GlobalAlloc(GMEM_MOVEABLE, (strlen(text) + 1)*2); + glob = GlobalAlloc(GMEM_MOVEABLE, (strlen(text) + 1)*4); if (glob) { tempw = GlobalLock(glob); @@ -1771,8 +1786,20 @@ void Sys_SaveClipboard(char *text) int error; while(*text) { - //NOTE: should be \r\n and not just \n - *tempw++ = utf8_decode(&error, text, &text); + codepoint = utf8_decode(&error, text, &text); + if (codepoint == '\n') + { //windows is stupid and annoying. + *tempw++ = '\r'; + *tempw++ = '\n'; + } + else if (codepoint > 0xffff) + { //and badly designed, too. + codepoint -= 0x10000; + *tempw++ = 0xD800 | ((codepoint>>10)&0x3ff); + *tempw++ = 0xDC00 | (codepoint&0x3ff); + } + else + *tempw++ = codepoint; } *tempw = 0; GlobalUnlock(glob); @@ -2818,9 +2845,10 @@ BOOL MoveFileU(const char *src, const char *dst) qboolean Sys_CheckUpdated(char *bindir, size_t bindirsize) { + wchar_t wide1[2048]; int ffe = COM_CheckParm("--fromfrontend"); PROCESS_INFORMATION childinfo; - STARTUPINFO startinfo = {sizeof(startinfo)}; + STARTUPINFOW startinfo = {sizeof(startinfo)}; char *e; strtoul(SVNREVISIONSTR, &e, 10); @@ -2847,7 +2875,6 @@ qboolean Sys_CheckUpdated(char *bindir, size_t bindirsize) else if (!ffe) { //if we're not from the frontend (ie: we ARE the frontend), we should run the updated build instead - char frontendpath[MAX_OSPATH]; char pendingpath[MAX_OSPATH]; char updatedpath[MAX_OSPATH]; @@ -2881,9 +2908,10 @@ qboolean Sys_CheckUpdated(char *bindir, size_t bindirsize) if (*updatedpath) { -//fixme: unicode paths/commandline. - GetModuleFileName(NULL, frontendpath, sizeof(frontendpath)-1); - if (CreateProcess(updatedpath, va("%s --fromfrontend \"%s\" \"%s\"", GetCommandLineA(), SVNREVISIONSTR, frontendpath), NULL, NULL, TRUE, 0, NULL, NULL, &startinfo, &childinfo)) + wchar_t widefe[MAX_OSPATH], wargs[2048]; + GetModuleFileNameW(NULL, widefe, countof(widefe)-1); + _snwprintf(wargs, countof(wargs), L"%s --fromfrontend \"%s\" \"%s\"", GetCommandLineW(), widen(wide1, sizeof(wide1), SVNREVISIONSTR), widefe); + if (CreateProcessW(widen(wide1, sizeof(wide1), updatedpath), wargs, NULL, NULL, TRUE, 0, NULL, NULL, &startinfo, &childinfo)) return true; } } @@ -2894,7 +2922,6 @@ qboolean Sys_CheckUpdated(char *bindir, size_t bindirsize) //com_argv[ffe+2] is frontend location if (atoi(com_argv[ffe+1]) > atoi(SVNREVISIONSTR)) { - wchar_t wide1[2048]; //ping-pong it back, to make sure we're running the most recent version. // GetModuleFileName(NULL, frontendpath, sizeof(frontendpath)-1); if (CreateProcessW(widen(wide1, sizeof(wide1), com_argv[ffe+2]), GetCommandLineW(), NULL, NULL, TRUE, 0, NULL, NULL, &startinfo, &childinfo)) diff --git a/engine/client/textedit.c b/engine/client/textedit.c index 4f8aaefa0..edbb1aec9 100644 --- a/engine/client/textedit.c +++ b/engine/client/textedit.c @@ -956,7 +956,7 @@ static void Draw_Line(int vy, fileblock_t *b, int cursorx) nnx-=(nnx - -viewportx)%ts; } else - nnx = Font_CharEndCoord(font_default, nx, (int)d[i] | (colour)); + nnx = Font_CharEndCoord(font_default, nx, colour, (int)d[i]); if (smx >= nx && smx <= nnx) { @@ -999,7 +999,7 @@ static void Draw_Line(int vy, fileblock_t *b, int cursorx) { if (d == c) { - int e = Font_DrawChar(nx, y, (int)0xe00b | (CON_WHITEMASK|CON_BLINKTEXT)); + int e = Font_DrawChar(nx, y, CON_WHITEMASK|CON_BLINKTEXT, (int)0xe00b); if (e >= vid.pixelwidth) viewportx += e - vid.pixelwidth; if (nx < 0) @@ -1015,12 +1015,12 @@ static void Draw_Line(int vy, fileblock_t *b, int cursorx) continue; } if (nx <= (int)vid.pixelwidth || cursorx>=0) - nnx = Font_DrawChar(nx, y, (int)*d | (colour)); + nnx = Font_DrawChar(nx, y, colour, (int)*d); else nnx = vid.pixelwidth; if (d == c) { - int e = Font_DrawChar(nx, y, (int)0xe00b | (CON_WHITEMASK|CON_BLINKTEXT)); + int e = Font_DrawChar(nx, y, CON_WHITEMASK|CON_BLINKTEXT, (int)0xe00b); if (e >= vid.pixelwidth) viewportx += e - vid.pixelwidth; if (nx < 0) @@ -1038,7 +1038,7 @@ static void Draw_Line(int vy, fileblock_t *b, int cursorx) /*we didn't do the cursor! stick it at the end*/ if (c && c >= d) { - int e = Font_DrawChar(nx, y, (int)0xe00b | (CON_WHITEMASK|CON_BLINKTEXT)); + int e = Font_DrawChar(nx, y, CON_WHITEMASK|CON_BLINKTEXT, (int)0xe00b); if (e >= vid.pixelwidth) viewportx += e - vid.pixelwidth; if (nx < 0) @@ -1058,7 +1058,7 @@ static void Draw_Line(int vy, fileblock_t *b, int cursorx) { if (*tooltip == '\n') break; - smx = Font_CharEndCoord(font_default, smx, *tooltip); + smx = Font_CharEndCoord(font_default, smx, (COLOR_CYAN<= 199901L + //C99 specifies that an inline function is used as a hint. there should be an actual body/copy somewhere (extern inline foo). + #define fte_inline inline //must have non-line 'int foo();' somewhere + #define fte_inlinebody extern inline +#elif defined(_MSC_VER) + //msvc will inline like C++. and that's fine. + #define fte_inline __inline //c++ style + #define fte_inlinebody +#elif (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5)) + //gcc will generally inline where it can - so long as its static. but that doesn't stop it warning + #define fte_inline __attribute__((unused)) static + #define fte_inlinebody static +#else + //make it static so we at least don't get errors (might still get warnings. see above) + #define fte_inline static + #define fte_inlinebody static +#endif + + #ifndef FTE_DEPRECATED #define FTE_DEPRECATED #endif diff --git a/engine/common/cmd.c b/engine/common/cmd.c index af8ae1812..8dded2c52 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -1318,8 +1318,8 @@ char *Cmd_ExpandString (char *data, char *dest, int destlen, int maxaccesslevel, int i, len; int quotes = 0; char *str; - char *bestmacro, *bestvar; - int name_length, macro_length, var_length; + char *bestvar; + int name_length, var_length; qboolean striptrailing; len = 0; @@ -1340,8 +1340,6 @@ char *Cmd_ExpandString (char *data, char *dest, int destlen, int maxaccesslevel, buf[0] = 0; buf[1] = 0; bestvar = NULL; - bestmacro = NULL; - macro_length=0; var_length = 0; while ((c = *data) > 32) { @@ -1350,21 +1348,13 @@ char *Cmd_ExpandString (char *data, char *dest, int destlen, int maxaccesslevel, data++; buf[i++] = c; buf[i] = 0; - if (!bestmacro) - { - if ((str = Cmd_ExpandCvar(buf+striptrailing, maxaccesslevel, &var_length))) - bestvar = str; - } - if (expandmacros && (str = TP_MacroString (buf+striptrailing, ¯o_length))) - bestmacro = str; + if (expandcvars && (str = Cmd_ExpandCvar(buf+striptrailing, maxaccesslevel, &var_length))) + bestvar = str; + if (expandmacros && (str = TP_MacroString (buf+striptrailing, &var_length))) + bestvar = str; } - if (bestmacro) - { - str = bestmacro; - name_length = macro_length; - } - else if (bestvar) + if (bestvar) { str = bestvar; name_length = var_length; @@ -3253,6 +3243,12 @@ static char *Macro_Quote (void) return "\""; } +static char *Macro_Random(void) +{ + Q_snprintfz(macro_buf, sizeof(macro_buf), "%u", rand()); + return macro_buf; +} + /* ============ Cmd_Init @@ -3307,6 +3303,7 @@ void Cmd_Init (void) Cmd_AddCommandD ("apropos", Cmd_Apropos_f, "Lists all cvars or commands with the specified substring somewhere in their name or descrition."); + Cmd_AddMacro("random", Macro_Random, true); Cmd_AddMacro("time", Macro_Time, true); Cmd_AddMacro("ukdate", Macro_UKDate, false); Cmd_AddMacro("usdate", Macro_USDate, false); diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index 43c243303..954ce4a73 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -1768,8 +1768,10 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in meshcache.vbo.svector = inf->vbo_skel_svector; meshcache.vbo.tvector = inf->vbo_skel_tvector; meshcache.vbo.colours[0] = inf->vborgba; - meshcache.vbo.bonenums = inf->vbo_skel_bonenum; - meshcache.vbo.boneweights = inf->vbo_skel_bweight; + memset(&meshcache.vbo.bonenums, 0, sizeof(meshcache.vbo.bonenums)); + memset(&meshcache.vbo.boneweights, 0, sizeof(meshcache.vbo.boneweights)); + meshcache.vbo.numbones = 0; + meshcache.vbo.bones = NULL; if (meshcache.vbo.indicies.sysptr) *vbop = meshcache.vbop = &meshcache.vbo; } @@ -1820,6 +1822,26 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in mesh->normals_array = inf->ofs_skel_norm; mesh->snormals_array = inf->ofs_skel_svect; mesh->tnormals_array = inf->ofs_skel_tvect; + + if (vbop) + { + meshcache.vbo.indicies = inf->vboindicies; + meshcache.vbo.indexcount = inf->numindexes; + meshcache.vbo.vertcount = inf->numverts; + meshcache.vbo.texcoord = inf->vbotexcoords; + meshcache.vbo.coord = inf->vbo_skel_verts; + memset(&meshcache.vbo.coord2, 0, sizeof(meshcache.vbo.coord2)); + meshcache.vbo.normals = inf->vbo_skel_normals; + meshcache.vbo.svector = inf->vbo_skel_svector; + meshcache.vbo.tvector = inf->vbo_skel_tvector; + meshcache.vbo.colours[0] = inf->vborgba; + meshcache.vbo.bonenums = inf->vbo_skel_bonenum; + meshcache.vbo.boneweights = inf->vbo_skel_bweight; + meshcache.vbo.numbones = inf->numbones; + meshcache.vbo.bones = meshcache.usebonepose; + if (meshcache.vbo.indicies.sysptr) + *vbop = meshcache.vbop = &meshcache.vbo; + } } } else @@ -1918,6 +1940,13 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in meshcache.vbo.vertcount = inf->numverts; meshcache.vbo.texcoord = inf->vbotexcoords; +#ifdef SKELETALMODELS + memset(&meshcache.vbo.bonenums, 0, sizeof(meshcache.vbo.bonenums)); + memset(&meshcache.vbo.boneweights, 0, sizeof(meshcache.vbo.boneweights)); + meshcache.vbo.numbones = 0; + meshcache.vbo.bones = 0; +#endif + #ifdef SERVERONLY mesh->xyz_array = p1->ofsverts; mesh->xyz2_array = NULL; @@ -6228,7 +6257,7 @@ static qboolean IQM_ImportArray4B(qbyte *base, struct iqmvertexarray *src, byte_ unsigned int fmt = LittleLong(src->format); unsigned int offset = LittleLong(src->offset); qboolean invalid = false; - maxval = min(256,maxval); + maxval = min(256,maxval); //output is bytes. if (!offset) { sz = 0; @@ -6447,6 +6476,8 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer, size_t fsize) vec4_t defaultweight = {0,0,0,0}; vec4_t defaultvert = {0,0,0,1}; + struct iqmbounds *inbounds; + int memsize; qbyte *obase=NULL; vecV_t *opos=NULL; @@ -6752,6 +6783,24 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer, size_t fsize) } free(framegroups); + //determine the bounds + inbounds = (struct iqmbounds*)(buffer + h->ofs_bounds); + for (i = 0; i < h->num_frames; i++) + { + vec3_t mins, maxs; + mins[0] = LittleFloat(inbounds[i].bbmin[0]); + mins[1] = LittleFloat(inbounds[i].bbmin[1]); + mins[2] = LittleFloat(inbounds[i].bbmin[2]); + AddPointToBounds(mins, mod->mins, mod->maxs); + maxs[0] = LittleFloat(inbounds[i].bbmax[0]); + maxs[1] = LittleFloat(inbounds[i].bbmax[1]); + maxs[2] = LittleFloat(inbounds[i].bbmax[2]); + AddPointToBounds(maxs, mod->mins, mod->maxs); + } + //fixme: shouldn't really be needed for an animated model + for (i = 0; i < h->num_vertexes; i++) + AddPointToBounds(opos[i], mod->mins, mod->maxs); + for (i = 0; i < h->num_meshes; i++) { gai[i].nextsurf = (i == (h->num_meshes-1))?NULL:&gai[i+1]; @@ -6799,9 +6848,9 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer, size_t fsize) gai[i].ofs_indexes = idx; for (t = 0; t < nt; t++) { - *idx++ = LittleShort(tris[t].vertex[0]) - offset; - *idx++ = LittleShort(tris[t].vertex[1]) - offset; - *idx++ = LittleShort(tris[t].vertex[2]) - offset; + *idx++ = LittleLong(tris[t].vertex[0]) - offset; + *idx++ = LittleLong(tris[t].vertex[1]) - offset; + *idx++ = LittleLong(tris[t].vertex[2]) - offset; } /*verts*/ @@ -6866,10 +6915,11 @@ qboolean Mod_ParseIQMAnim(char *buffer, galiasinfo_t *prototype, void**poseofs, qboolean QDECL Mod_LoadInterQuakeModel(model_t *mod, void *buffer, size_t fsize) { - int i; galiasinfo_t *root; struct iqmheader *h = (struct iqmheader *)buffer; + ClearBounds(mod->mins, mod->maxs); + root = Mod_ParseIQMMeshModel(mod, buffer, fsize); if (!root) { @@ -6878,9 +6928,7 @@ qboolean QDECL Mod_LoadInterQuakeModel(model_t *mod, void *buffer, size_t fsize) mod->flags = h->flags; - ClearBounds(mod->mins, mod->maxs); - for (i = 0; i < root->numverts; i++) - AddPointToBounds(root->ofs_skel_xyz[i], mod->mins, mod->maxs); + mod->radius = RadiusFromBounds(mod->mins, mod->maxs); Mod_ClampModelSize(mod); diff --git a/engine/common/common.c b/engine/common/common.c index 57e37bb1f..dc8cf0136 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -2545,6 +2545,7 @@ conchar_t q3codemasks[MAXQ3COLOURS] = { //Converts a conchar_t string into a char string. returns the null terminator. pass NULL for stop to calc it char *COM_DeFunString(conchar_t *str, conchar_t *stop, char *out, int outsize, qboolean ignoreflags) { + unsigned int codeflags, codepoint; if (!stop) { for (stop = str; *stop; stop++) @@ -2575,12 +2576,13 @@ char *COM_DeFunString(conchar_t *str, conchar_t *stop, char *out, int outsize, q fl = CON_WHITEMASK; while(str < stop) { - if ((*str & CON_HIDDEN) && ignoreflags) + str = Font_Decode(str, &codeflags, &codepoint); + if ((codeflags & CON_HIDDEN) && ignoreflags) { str++; continue; } - if (*str == CON_LINKSTART) + if (codeflags == (CON_LINKSPECIAL | CON_HIDDEN) && codepoint == '[') { if (!ignoreflags) { @@ -2595,7 +2597,7 @@ char *COM_DeFunString(conchar_t *str, conchar_t *stop, char *out, int outsize, q str++; continue; } - else if (*str == CON_LINKEND) + else if (codeflags == (CON_LINKSPECIAL | CON_HIDDEN) && codepoint == ']') { if (!ignoreflags) { @@ -2609,9 +2611,9 @@ char *COM_DeFunString(conchar_t *str, conchar_t *stop, char *out, int outsize, q str++; continue; } - else if ((*str & CON_FLAGSMASK) != fl && !ignoreflags) + else if (codeflags != fl && !ignoreflags) { - d = fl^(*str & CON_FLAGSMASK); + d = fl^codeflags; // if (fl & CON_NONCLEARBG) //not represented. if (d & CON_BLINKTEXT) { @@ -2644,15 +2646,15 @@ char *COM_DeFunString(conchar_t *str, conchar_t *stop, char *out, int outsize, q 0, 0, 0, 0, 0, '4', '2', '5', '1', '6', '3', '7'}; - if (!(d & (CON_BGMASK | CON_NONCLEARBG)) && q3[(*str & CON_FGMASK) >> CON_FGSHIFT] && !((d|fl) & CON_HALFALPHA)) + if (!(d & (CON_BGMASK | CON_NONCLEARBG)) && q3[(codeflags & CON_FGMASK) >> CON_FGSHIFT] && !((d|fl) & CON_HALFALPHA)) { if (outsize<=2) break; outsize -= 2; - d = (*str & CON_FLAGSMASK); + d = codeflags; *out++ = '^'; - *out++ = q3[(*str & CON_FGMASK) >> CON_FGSHIFT]; + *out++ = q3[(codeflags & CON_FGMASK) >> CON_FGSHIFT]; } else { @@ -2660,7 +2662,7 @@ char *COM_DeFunString(conchar_t *str, conchar_t *stop, char *out, int outsize, q break; outsize -= 4; - d = (*str & CON_FLAGSMASK); + d = codeflags; *out++ = '^'; *out++ = '&'; if ((d & CON_FGMASK) == CON_WHITEMASK) @@ -2676,14 +2678,14 @@ char *COM_DeFunString(conchar_t *str, conchar_t *stop, char *out, int outsize, q } } - fl = (*str & CON_FLAGSMASK); + fl = codeflags; } //don't magically show hidden text - if (ignoreflags && (*str & CON_HIDDEN)) + if (ignoreflags && (codeflags & CON_HIDDEN)) continue; - c = unicode_encode(out, (*str++ & CON_CHARMASK), outsize-1, !ignoreflags); + c = unicode_encode(out, codepoint, outsize-1, !ignoreflags); if (!c) break; outsize -= c; @@ -2979,10 +2981,18 @@ conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t } else { - if (uc > CON_CHARMASK) + if (uc > 0x10ffff) uc = 0xfffd; if (!--outsize) break; + + if (uc > 0xffff) + { + if (!--outsize) + break; + *out++ = uc>>16 | CON_LONGCHAR | (ext & CON_HIDDEN); + uc &= 0xffff; + } *out++ = uc | ext; str = end; continue; @@ -3156,11 +3166,18 @@ conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t if (str[len] == '}') len++; - if (uc > CON_CHARMASK) + if (uc > 0x10ffff) //utf-16 imposes a limit on standard unicode codepoints (any encoding) uc = 0xfffd; if (!--outsize) break; + if (uc > 0xffff) //utf-16 imposes a limit on standard unicode codepoints (any encoding) + { + if (!--outsize) + break; + *out++ = uc>>16 | CON_LONGCHAR | (ext & CON_HIDDEN); + uc &= 0xffff; + } *out++ = uc | ext; str += len; diff --git a/engine/common/console.h b/engine/common/console.h index c3e117dcc..02c744c0a 100644 --- a/engine/common/console.h +++ b/engine/common/console.h @@ -31,10 +31,12 @@ extern consolecolours_t consolecolours[MAXCONCOLOURS]; #define MAXQ3COLOURS 10 extern conchar_t q3codemasks[MAXQ3COLOURS]; +#define CON_LONGCHAR_MASK (CON_LONGCHAR|CON_RICHFORECOLOUR) + #define CON_NONCLEARBG 0x00800000 //disabled if CON_RICHFORECOLOUR #define CON_HALFALPHA 0x00400000 //disabled if CON_RICHFORECOLOUR #define CON_LINKSPECIAL 0x00200000 //disabled if CON_RICHFORECOLOUR -#define CON_UNUSED1 0x00100000 //disabled if CON_RICHFORECOLOUR +#define CON_LONGCHAR 0x00100000 //flags (other than hidden) are found in the following conchar. disabled if CON_RICHFORECOLOUR #define CON_HIDDEN 0x00080000 #define CON_BLINKTEXT 0x00040000 #define CON_2NDCHARSETTEXT 0x00020000 diff --git a/engine/common/cvar.c b/engine/common/cvar.c index 2be25de4e..f90db4c14 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -1232,7 +1232,6 @@ qboolean Cvar_Command (int level) char *str; char buffer[65536]; int olev; - int seat; // check variables v = Cvar_FindVar (Cmd_Argv(0)); @@ -1301,9 +1300,9 @@ qboolean Cvar_Command (int level) } #ifndef SERVERONLY - seat = CL_TargettedSplit(true); if (v->flags & CVAR_USERINFO) { + int seat = CL_TargettedSplit(true); if (Cmd_FromGamecode() && cls.protocol == CP_QUAKEWORLD) { //don't bother even changing the cvar locally, just update the server's version. //fixme: quake2/quake3 latching. diff --git a/engine/common/particles.h b/engine/common/particles.h index e2ae1d8d6..246af410b 100644 --- a/engine/common/particles.h +++ b/engine/common/particles.h @@ -52,12 +52,6 @@ extern int #endif rtqw_railtrail, //common to zquake/fuhquake/fte - rtfte_lightning1, - ptfte_lightning1_end, - rtfte_lightning2, - ptfte_lightning2_end, - rtfte_lightning3, - ptfte_lightning3_end, ptfte_bullet, ptfte_superbullet; diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 75e161749..464e6edc7 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -1207,12 +1207,14 @@ typedef struct q1usercmd_s #define Q2RDF_IRGOGGLES (1u<<2) //ents with Q2RF_IR_VISIBLE show up pure red. #define Q2RDF_UVGOGGLES (1u<<3) //usused / reserved //ROGUE + #define RDF_BLOOM (1u<<16) #define RDF_FISHEYE (1u<<17) #define RDF_WATERWARP (1u<<18) #define RDF_CUSTOMPOSTPROC (1u<<19) +#define RDF_ANTIALIAS (1u<<20) //fxaa, or possibly even just fsaa -#define RDF_ALLPOSTPROC (RDF_BLOOM|RDF_FISHEYE|RDF_WATERWARP|RDF_CUSTOMPOSTPROC) //these flags require rendering to an fbo for the various different post-processing shaders. +#define RDF_ALLPOSTPROC (RDF_BLOOM|RDF_FISHEYE|RDF_WATERWARP|RDF_CUSTOMPOSTPROC|RDF_ANTIALIAS) //these flags require rendering to an fbo for the various different post-processing shaders. diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index f15fd2d59..a66bc91f6 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -2859,6 +2859,7 @@ static void DrawPass(const shaderpass_t *pass) //second pass should be an ADD //this depends upon rgbgens for light levels, so each pass *must* be pushed to hardware individually +#if MAXRLIGHTMAPS > 1 for (j = 1; j < MAXRLIGHTMAPS && shaderstate.curbatch->lightmap[j] >= 0; j++) { if (j == 1) @@ -2901,6 +2902,7 @@ static void DrawPass(const shaderpass_t *pass) tmu++; } +#endif //might need to break the pass here if (j > 1 && i != lastpass) @@ -3211,6 +3213,24 @@ static void BE_Program_Set_Attributes(const program_t *prog, unsigned int perm, qglUniform3fvARB(ph, 1, param4); break; + case SP_SOURCESIZE: + if (shaderstate.tex_sourcecol) + { + param4[0] = shaderstate.tex_sourcecol->width; + param4[1] = shaderstate.tex_sourcecol->height; + } + else if (shaderstate.tex_sourcedepth) + { + param4[0] = shaderstate.tex_sourcedepth->width; + param4[1] = shaderstate.tex_sourcedepth->height; + } + else + { + param4[0] = 1; + param4[1] = 1; + } + qglUniform2fvARB(ph, 1, param4); + break; case SP_RENDERTEXTURESCALE: if (sh_config.texture_non_power_of_two_pic) { diff --git a/engine/gl/gl_bloom.c b/engine/gl/gl_bloom.c index c5c7a1e39..84fb82303 100644 --- a/engine/gl/gl_bloom.c +++ b/engine/gl/gl_bloom.c @@ -46,13 +46,16 @@ http://prideout.net/archive/bloom/ contains some sample code #include "glquake.h" cvar_t r_bloom = CVARAFD("r_bloom", "0", "gl_bloom", CVAR_ARCHIVE, "Enables bloom (light bleeding from bright objects). Fractional values reduce the amount shown."); cvar_t r_bloom_filter = CVARD("r_bloom_filter", "0.7 0.7 0.7", "Controls how bright the image must get before it will bloom (3 separate values, in RGB order)."); -cvar_t r_bloom_scale = CVARD("r_bloom_scale", "0.5", "Controls the initial downscale size. Smaller values will bloom further but be more random."); +cvar_t r_bloom_size = CVARD("r_bloom_size", "4", "Target bloom kernel size (assuming a video width of 320)."); +cvar_t r_bloom_downsize = CVARD("r_bloom_downsize", "0", "Technically more correct with a value of 1, but you probably won't notice."); +cvar_t r_bloom_initialscale = CVARD("r_bloom_initialscale", "1", "Initial scaling factor for bloom. should be either 1 or 0.5"); + static shader_t *bloomfilter; static shader_t *bloomrescale; static shader_t *bloomblur; static shader_t *bloomfinal; -#define MAXLEVELS 3 //presumably this could be up to 16, but that will be too expensive. +#define MAXLEVELS 16 texid_t pingtex[2][MAXLEVELS]; fbostate_t fbo_bloom; static int scrwidth, scrheight; @@ -78,24 +81,23 @@ void R_BloomRegister(void) { Cvar_Register (&r_bloom, "bloom"); Cvar_Register (&r_bloom_filter, "bloom"); - Cvar_Register (&r_bloom_scale, "bloom"); + Cvar_Register (&r_bloom_size, "bloom"); + Cvar_Register (&r_bloom_downsize, "bloom"); + Cvar_Register (&r_bloom_initialscale, "bloom"); } static void R_SetupBloomTextures(int w, int h) { int i, j; - char name[64]; - if (w == scrwidth && h == scrheight && !r_bloom_scale.modified) + if (w == scrwidth && h == scrheight && !r_bloom_initialscale.modified) return; - r_bloom_scale.modified = false; + r_bloom_initialscale.modified = false; scrwidth = w; scrheight = h; //I'm depending on npot here - w *= r_bloom_scale.value; - h *= r_bloom_scale.value; + w *= r_bloom_initialscale.value; + h *= r_bloom_initialscale.value; for (i = 0; i < MAXLEVELS; i++) { - w /= 2; - h /= 2; /*I'm paranoid*/ if (w < 4) w = 4; @@ -104,19 +106,19 @@ static void R_SetupBloomTextures(int w, int h) texwidth[i] = w; texheight[i] = h; + + w /= 2; + h /= 2; } - /*now create textures for each level*/ + /*destroy textures for each level, to ensure they're created fresh as needed*/ for (j = 0; j < MAXLEVELS; j++) { for (i = 0; i < 2; i++) { - if (!TEXVALID(pingtex[i][j])) - { - sprintf(name, "***bloom*%c*%i***", 'a'+i, j); - TEXASSIGN(pingtex[i][j], Image_CreateTexture(name, NULL, IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR)); - } - Image_Upload(pingtex[i][j], TF_RGBA32, NULL, NULL, texwidth[j], texheight[j], IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR); + if (TEXVALID(pingtex[i][j])) + Image_UnloadTexture(pingtex[i][j]); + pingtex[i][j] = r_nulltex; } } @@ -162,9 +164,6 @@ static void R_SetupBloomTextures(int w, int h) "map $upperoverlay\n" "}\n" "}\n"); - bloomfinal->defaulttextures->base = pingtex[0][0]; - bloomfinal->defaulttextures->loweroverlay = pingtex[0][1]; - bloomfinal->defaulttextures->upperoverlay = pingtex[0][2]; } qboolean R_CanBloom(void) { @@ -184,6 +183,12 @@ void R_BloomBlend (texid_t source, int x, int y, int w, int h) { int i; int oldfbo = 0; + texid_t intex; + int pixels = 1; + int targetpixels = r_bloom_size.value * vid.pixelwidth / 320; + char name[64]; + + targetpixels *= r_bloom_initialscale.value; /*whu?*/ if (!w || !h) @@ -192,52 +197,76 @@ void R_BloomBlend (texid_t source, int x, int y, int w, int h) /*update textures if we need to resize them*/ R_SetupBloomTextures(w, h); - for (i = 0; i < MAXLEVELS; i++) + /*filter the screen into a downscaled image*/ + if (!TEXVALID(pingtex[0][0])) { - if (i == 0) + sprintf(name, "***bloom*%c*%i***", 'a'+0, 0); + TEXASSIGN(pingtex[0][0], Image_CreateTexture(name, NULL, IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR)); + Image_Upload(pingtex[0][0], TF_RGBA32, NULL, NULL, texwidth[0], texheight[0], IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR); + } + oldfbo = GLBE_FBO_Update(&fbo_bloom, 0, &pingtex[0][0], 1, r_nulltex, 0, 0, 0); + GLBE_FBO_Sources(source, r_nulltex); + qglViewport (0, 0, texwidth[0], texheight[0]); + R2D_ScalePic(0, vid.height, vid.width, -(int)vid.height, bloomfilter); + + intex = pingtex[0][0]; + + for (pixels = 1, i = 0; pixels < targetpixels && i < MAXLEVELS; i++, pixels <<= 1) + { + /*create any textures if they're not valid yet*/ + if (!TEXVALID(pingtex[0][i])) { - /*filter the screen into a downscaled image*/ - oldfbo = GLBE_FBO_Update(&fbo_bloom, 0, &pingtex[0][0], 1, r_nulltex, 0, 0, 0); - GLBE_FBO_Sources(source, r_nulltex); - qglViewport (0, 0, texwidth[0], texheight[0]); - R2D_ScalePic(0, vid.height, vid.width, -(int)vid.height, bloomfilter); + sprintf(name, "***bloom*%c*%i***", 'a'+0, i); + TEXASSIGN(pingtex[0][i], Image_CreateTexture(name, NULL, IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR)); + Image_Upload(pingtex[0][i], TF_RGBA32, NULL, NULL, texwidth[i], texheight[i], IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR); } - else + if (!TEXVALID(pingtex[1][i])) + { + sprintf(name, "***bloom*%c*%i***", 'a'+1, i); + TEXASSIGN(pingtex[1][i], Image_CreateTexture(name, NULL, IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR)); + Image_Upload(pingtex[1][i], TF_RGBA32, NULL, NULL, texwidth[i], texheight[i], IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR); + } + + //downsize the blur, for added accuracy + if (i > 0 && r_bloom_downsize.ival) { /*simple downscale that multiple times*/ GLBE_FBO_Update(&fbo_bloom, 0, &pingtex[0][i], 1, r_nulltex, 0, 0, 0); GLBE_FBO_Sources(pingtex[0][i-1], r_nulltex); qglViewport (0, 0, texwidth[i], texheight[i]); R2D_ScalePic(0, vid.height, vid.width, -(int)vid.height, bloomrescale); + intex = pingtex[0][i]; + r_worldentity.glowmod[0] = 1.0 / intex->width; } -// } -// for (i = 0; i < MAXLEVELS; i++) -// { - /*gaussian filter the mips to bloom more smoothly - the blur is done with two passes. first samples horizontally then vertically. - the 1.2 pixels thing gives us a 5*5 filter by weighting the edge accordingly - */ - r_worldentity.glowmod[0] = 1.2 / texwidth[i]; + else + r_worldentity.glowmod[0] = 2.0 / intex->width; + r_worldentity.glowmod[1] = 0; GLBE_FBO_Update(&fbo_bloom, 0, &pingtex[1][i], 1, r_nulltex, 0, 0, 0); - GLBE_FBO_Sources(pingtex[0][i], r_nulltex); - qglViewport (0, 0, texwidth[i], texheight[i]); + GLBE_FBO_Sources(intex, r_nulltex); + qglViewport (0, 0, pingtex[1][i]->width, pingtex[1][i]->height); BE_SelectEntity(&r_worldentity); R2D_ScalePic(0, vid.height, vid.width, -(int)vid.height, bloomblur); r_worldentity.glowmod[0] = 0; - r_worldentity.glowmod[1] = 1.2 / texheight[i]; + r_worldentity.glowmod[1] = 1.0 / pingtex[1][i]->height; GLBE_FBO_Update(&fbo_bloom, 0, &pingtex[0][i], 1, r_nulltex, 0, 0, 0); GLBE_FBO_Sources(pingtex[1][i], r_nulltex); - qglViewport (0, 0, texwidth[i], texheight[i]); + qglViewport (0, 0, pingtex[0][i]->width, pingtex[0][i]->height); BE_SelectEntity(&r_worldentity); R2D_ScalePic(0, vid.height, vid.width, -(int)vid.height, bloomblur); + + intex = pingtex[0][i]; } r_worldentity.glowmod[0] = 0; r_worldentity.glowmod[1] = 0; GL_Set2D(false); + bloomfinal->defaulttextures->base = intex; + bloomfinal->defaulttextures->loweroverlay = (i >= 2)?pingtex[0][i-2]:0; + bloomfinal->defaulttextures->upperoverlay = (i >= 3)?pingtex[0][i-3]:0; + /*combine them onto the screen*/ GLBE_FBO_Pop(oldfbo); GLBE_FBO_Sources(source, r_nulltex); diff --git a/engine/gl/gl_font.c b/engine/gl/gl_font.c index d9ed09e33..8fcf86541 100644 --- a/engine/gl/gl_font.c +++ b/engine/gl/gl_font.c @@ -17,11 +17,11 @@ void Font_BeginScaledString(struct font_s *font, float vx, float vy, float szx, void Font_Transform(float vx, float vy, int *px, int *py); int Font_CharHeight(void); float Font_CharScaleHeight(void); -int Font_CharWidth(unsigned int charcode); -float Font_CharScaleWidth(unsigned int charcode); -int Font_CharEndCoord(struct font_s *font, int x, unsigned int charcode); -int Font_DrawChar(int px, int py, unsigned int charcode); -float Font_DrawScaleChar(float px, float py, unsigned int charcode); /*avoid using*/ +int Font_CharWidth(unsigned int charflags, unsigned int codepoint); +float Font_CharScaleWidth(unsigned int charflags, unsigned int codepoint); +int Font_CharEndCoord(struct font_s *font, int x, unsigned int charflags, unsigned int codepoint); +int Font_DrawChar(int px, int py, unsigned int charflags, unsigned int codepoint); +float Font_DrawScaleChar(float px, float py, unsigned int charflags, unsigned int codepoint); /*avoid using*/ void Font_EndString(struct font_s *font); int Font_LineBreaks(conchar_t *start, conchar_t *end, int maxpixelwidth, int maxlines, conchar_t **starts, conchar_t **ends); struct font_s *font_default; @@ -29,6 +29,10 @@ struct font_s *font_console; struct font_s *font_tiny; extern unsigned int r2d_be_flags; +//by adding 'extern' to one definition of a function in a translation unit, then the definition in that TU is NOT considered an inline definition. meaning non-inlined references in other TUs can link to it instead of their own if needed. +fte_inlinebody conchar_t *Font_Decode(conchar_t *start, unsigned int *codeflags, unsigned int *codepoint); + + #ifdef AVAIL_FREETYPE #include #include FT_FREETYPE_H @@ -718,9 +722,15 @@ static struct charcache_s *Font_TryLoadGlyph(font_t *f, CHARIDXTYPE charidx) } //obtains a cached char, null if not cached -static struct charcache_s *Font_GetChar(font_t *f, CHARIDXTYPE charidx) +static struct charcache_s *Font_GetChar(font_t *f, unsigned int codepoint) { - struct charcache_s *c = &f->chars[charidx]; + CHARIDXTYPE charidx; + struct charcache_s *c; + if (codepoint > CON_CHARMASK) + charidx = 0xfffd; + else + charidx = codepoint; + c = &f->chars[charidx]; if (c->texplane == INVALIDPLANE) { if (charidx >= TRACKERFIRST && charidx < TRACKERFIRST+100) @@ -1575,19 +1585,20 @@ float Font_CharScaleHeight(void) This is where the character ends. Note: this function supports tabs - x must always be based off 0, with Font_LineDraw actually used to draw the line. */ -int Font_CharEndCoord(struct font_s *font, int x, unsigned int charcode) +int Font_CharEndCoord(struct font_s *font, int x, unsigned int charflags, unsigned int codepoint) { struct charcache_s *c; + #define TABWIDTH (8*20) - if (charcode&CON_HIDDEN) + if (charflags&CON_HIDDEN) return x; - if ((charcode&CON_CHARMASK) == '\t') + if (codepoint == '\t') return x + ((TABWIDTH - (x % TABWIDTH)) % TABWIDTH); - if ((charcode & CON_2NDCHARSETTEXT) && font->alt) + if ((charflags & CON_2NDCHARSETTEXT) && font->alt) font = font->alt; - c = Font_GetChar(font, (CHARIDXTYPE)(charcode&CON_CHARMASK)); + c = Font_GetChar(font, codepoint); if (!c) { return x+0; @@ -1598,16 +1609,18 @@ int Font_CharEndCoord(struct font_s *font, int x, unsigned int charcode) //obtains the width of a character from a given font. This is how wide it is. The next char should be drawn at x + result. //FIXME: this function cannot cope with tab and should not be used. -int Font_CharWidth(unsigned int charcode) +int Font_CharWidth(unsigned int charflags, unsigned int codepoint) { struct charcache_s *c; struct font_s *font = curfont; - if (charcode&CON_HIDDEN) + + if (charflags&CON_HIDDEN) return 0; - if ((charcode & CON_2NDCHARSETTEXT) && font->alt) + + if ((charflags & CON_2NDCHARSETTEXT) && font->alt) font = font->alt; - c = Font_GetChar(curfont, (CHARIDXTYPE)(charcode&CON_CHARMASK)); + c = Font_GetChar(curfont, codepoint); if (!c) { return 0; @@ -1618,16 +1631,17 @@ int Font_CharWidth(unsigned int charcode) //obtains the width of a character from a given font. This is how wide it is. The next char should be drawn at x + result. //FIXME: this function cannot cope with tab and should not be used. -float Font_CharScaleWidth(unsigned int charcode) +float Font_CharScaleWidth(unsigned int charflags, unsigned int codepoint) { struct charcache_s *c; struct font_s *font = curfont; - if (charcode&CON_HIDDEN) + + if (charflags&CON_HIDDEN) return 0; - if ((charcode & CON_2NDCHARSETTEXT) && font->alt) + if ((charflags & CON_2NDCHARSETTEXT) && font->alt) font = font->alt; - c = Font_GetChar(curfont, (CHARIDXTYPE)(charcode&CON_CHARMASK)); + c = Font_GetChar(curfont, codepoint); if (!c) { return 0; @@ -1636,6 +1650,29 @@ float Font_CharScaleWidth(unsigned int charcode) return c->advance * curfont_scale[0]; } +conchar_t *Font_DecodeReverse(conchar_t *start, conchar_t *stop, unsigned int *codeflags, unsigned int *codepoint) +{ + if (start <= stop) + { + *codeflags = 0; + *codepoint = 0; + return stop; + } + + start--; + if (start > stop && start[-1] & CON_LONGCHAR) + if (!(start[-1] & CON_RICHFORECOLOUR)) + { + start--; + *codeflags = start[1]; + *codepoint = ((start[0] & CON_CHARMASK)<<16) | (start[1] & CON_CHARMASK); + return start; + } + *codeflags = start[0]; + *codepoint = start[0] & CON_CHARMASK; + return start; +} + //for a given font, calculate the line breaks and word wrapping for a block of text //start+end are the input string //starts+ends are an array of line start and end points, which have maxlines elements. @@ -1643,41 +1680,49 @@ float Font_CharScaleWidth(unsigned int charcode) //maxpixelwidth is the width of the display area in pixels int Font_LineBreaks(conchar_t *start, conchar_t *end, int maxpixelwidth, int maxlines, conchar_t **starts, conchar_t **ends) { - int l, bt; + conchar_t *l, *bt, *n; int px; int foundlines = 0; struct font_s *font = curfont; + unsigned int codeflags, codepoint; while (start < end) { // scan the width of the line - for (px=0, l=0 ; px <= maxpixelwidth; ) + for (px=0, l=start ; px <= maxpixelwidth; ) { - if (start+l >= end || (start[l]&(CON_CHARMASK|CON_HIDDEN)) == '\n' || (start[l]&(CON_CHARMASK|CON_HIDDEN)) == '\v') + if (l >= end) break; - px = Font_CharEndCoord(font, px, start[l]); - l++; + n = Font_Decode(l, &codeflags, &codepoint); + if (!(codeflags & CON_HIDDEN) && (codepoint == '\n' || codepoint == '\v')) + break; + px = Font_CharEndCoord(font, px, codeflags, codepoint); + l = n; } //if we did get to the end if (px > maxpixelwidth) { bt = l; //backtrack until we find a space - while(l > 0 && (start[l-1]&CON_CHARMASK)>' ') + for(;;) { - l--; + n = Font_DecodeReverse(l, start, &codeflags, &codepoint); + if (codepoint > ' ') + l = n; + else + break; } - if (l == 0 && bt>0) - l = bt-1; + if (l == start && bt>start) + l = Font_DecodeReverse(bt, start, &codeflags, &codepoint); } starts[foundlines] = start; - ends[foundlines] = start+l; + ends[foundlines] = l; foundlines++; if (foundlines == maxlines) break; - start+=l; + start=l; if (start == end) break; @@ -1693,9 +1738,11 @@ int Font_LineWidth(conchar_t *start, conchar_t *end) //fixme: does this do the right thing with tabs? int x = 0; struct font_s *font = curfont; + unsigned int codeflags, codepoint; for (; start < end; start++) { - x = Font_CharEndCoord(font, x, *start); + start = Font_Decode(start, &codeflags, &codepoint); + x = Font_CharEndCoord(font, x, codeflags, codepoint); } return x; } @@ -1703,9 +1750,11 @@ float Font_LineScaleWidth(conchar_t *start, conchar_t *end) { int x = 0; struct font_s *font = curfont; - for (; start < end; start++) + unsigned int codeflags, codepoint; + for (; start < end; start) { - x = Font_CharEndCoord(font, x, *start); + start = Font_Decode(start, &codeflags, &codepoint); + x = Font_CharEndCoord(font, x, codeflags, codepoint); } return x * curfont_scale[0]; } @@ -1713,10 +1762,12 @@ void Font_LineDraw(int x, int y, conchar_t *start, conchar_t *end) { int lx = 0; struct font_s *font = curfont; - for (; start < end; start++) + unsigned int codeflags, codepoint; + for (; start < end; ) { - Font_DrawChar(x+lx, y, *start); - lx = Font_CharEndCoord(font, lx, *start); + start = Font_Decode(start, &codeflags, &codepoint); + Font_DrawChar(x+lx, y, codeflags, codepoint); + lx = Font_CharEndCoord(font, lx, codeflags, codepoint); } } @@ -1747,7 +1798,7 @@ void Font_InvalidateColour(void) } //draw a character from the current font at a pixel location. -int Font_DrawChar(int px, int py, unsigned int charcode) +int Font_DrawChar(int px, int py, unsigned int charflags, unsigned int codepoint) { struct charcache_s *c; float s0, s1; @@ -1762,30 +1813,30 @@ int Font_DrawChar(int px, int py, unsigned int charcode) #else #define dxbias 0 #endif - if (charcode & CON_HIDDEN) + if (charflags & CON_HIDDEN) return px; - if (charcode & CON_2NDCHARSETTEXT) + if (charflags & CON_2NDCHARSETTEXT) { if (font->alt) { font = font->alt; -// charcode &= ~CON_2NDCHARSETTEXT; +// charflags &= ~CON_2NDCHARSETTEXT; } - else if ((charcode&CON_CHARMASK) >= 0xe000 && (charcode&CON_CHARMASK) <= 0xe0ff) - charcode &= ~CON_2NDCHARSETTEXT; //don't double-dip + else if ((codepoint) >= 0xe000 && (codepoint) <= 0xe0ff) + charflags &= ~CON_2NDCHARSETTEXT; //don't double-dip } //crash if there is no current font. - c = Font_GetChar(font, (CHARIDXTYPE)(charcode&CON_CHARMASK)); + c = Font_GetChar(font, codepoint); if (!c) return px; nextx = px + c->advance; - if ((charcode & CON_CHARMASK) == '\t') + if (codepoint == '\t') return px + ((TABWIDTH - (px % TABWIDTH)) % TABWIDTH); - if ((charcode & CON_CHARMASK) == ' ') + if (codepoint == ' ') return nextx; /* if (charcode & CON_BLINKTEXT) @@ -1795,9 +1846,9 @@ int Font_DrawChar(int px, int py, unsigned int charcode) return nextx; } */ - if (charcode & CON_RICHFORECOLOUR) + if (charflags & CON_RICHFORECOLOUR) { - col = charcode & (CON_2NDCHARSETTEXT|CON_BLINKTEXT|CON_RICHFORECOLOUR|(0xfff<alttint[0]; rgba[1] *= font->alttint[1]; @@ -1830,7 +1881,7 @@ int Font_DrawChar(int px, int py, unsigned int charcode) rgba[1] *= font_foretint[1]; rgba[2] *= font_foretint[2]; rgba[3] *= font_foretint[3]; - if (charcode & CON_BLINKTEXT) + if (charflags & CON_BLINKTEXT) { float a = (sin(realtime*3)+1)*0.4 + 0.2; rgba[3] *= a; @@ -1843,7 +1894,7 @@ int Font_DrawChar(int px, int py, unsigned int charcode) } else { - col = charcode & (CON_2NDCHARSETTEXT|CON_NONCLEARBG|CON_BGMASK|CON_FGMASK|CON_HALFALPHA|CON_BLINKTEXT); + col = charflags & (CON_2NDCHARSETTEXT|CON_NONCLEARBG|CON_BGMASK|CON_FGMASK|CON_HALFALPHA|CON_BLINKTEXT); if (col != font_colourmask) { vec4_t rgba; @@ -1851,19 +1902,19 @@ int Font_DrawChar(int px, int py, unsigned int charcode) Font_Flush(); font_colourmask = col; - col = (charcode&CON_FGMASK)>>CON_FGSHIFT; + col = (charflags&CON_FGMASK)>>CON_FGSHIFT; rgba[0] = consolecolours[col].fr*255; rgba[1] = consolecolours[col].fg*255; rgba[2] = consolecolours[col].fb*255; - rgba[3] = (charcode & CON_HALFALPHA)?127:255; + rgba[3] = (charflags & CON_HALFALPHA)?127:255; - col = (charcode&CON_BGMASK)>>CON_BGSHIFT; + col = (charflags&CON_BGMASK)>>CON_BGSHIFT; font_backcolour[0] = consolecolours[col].fr*255; font_backcolour[1] = consolecolours[col].fg*255; font_backcolour[2] = consolecolours[col].fb*255; - font_backcolour[3] = (charcode & CON_NONCLEARBG)?127:0; + font_backcolour[3] = (charflags & CON_NONCLEARBG)?127:0; - if (charcode & CON_2NDCHARSETTEXT) + if (charflags & CON_2NDCHARSETTEXT) { rgba[0] *= font->alttint[0]; rgba[1] *= font->alttint[1]; @@ -1879,7 +1930,7 @@ int Font_DrawChar(int px, int py, unsigned int charcode) rgba[1] *= font_foretint[1]; rgba[2] *= font_foretint[2]; rgba[3] *= font_foretint[3]; - if (charcode & CON_BLINKTEXT) + if (charflags & CON_BLINKTEXT) { float a = (sin(realtime*3)+1)*0.4 + 0.2; rgba[3] *= a; @@ -1968,7 +2019,7 @@ int Font_DrawChar(int px, int py, unsigned int charcode) } /*there is no sane way to make this pixel-correct*/ -float Font_DrawScaleChar(float px, float py, unsigned int charcode) +float Font_DrawScaleChar(float px, float py, unsigned int charflags, unsigned int codepoint) { struct charcache_s *c; float s0, s1; @@ -1988,40 +2039,40 @@ float Font_DrawScaleChar(float px, float py, unsigned int charcode) // if (!curfont_scaled) // return Font_DrawChar(px, py, charcode); - if (charcode & CON_2NDCHARSETTEXT) + if (charflags & CON_2NDCHARSETTEXT) { if (font->alt) { font = font->alt; - charcode &= ~CON_2NDCHARSETTEXT; + charflags &= ~CON_2NDCHARSETTEXT; } - else if ((charcode&CON_CHARMASK) >= 0xe000 && (charcode&CON_CHARMASK) <= 0xe0ff) - charcode &= ~CON_2NDCHARSETTEXT; //don't double-dip + else if (codepoint >= 0xe000 && codepoint <= 0xe0ff) + charflags &= ~CON_2NDCHARSETTEXT; //don't double-dip } cw = curfont_scale[0]; ch = curfont_scale[1]; //crash if there is no current font. - c = Font_GetChar(font, (CHARIDXTYPE)(charcode&CON_CHARMASK)); + c = Font_GetChar(font, codepoint); if (!c) return px; nextx = px + c->advance*cw; - if ((charcode & CON_CHARMASK) == ' ') + if (codepoint == ' ') return nextx; - if (charcode & CON_BLINKTEXT) + if (charflags & CON_BLINKTEXT) { if (!cl_noblink.ival) if ((int)(realtime*3) & 1) return nextx; } - if (charcode & CON_RICHFORECOLOUR) + if (charflags & CON_RICHFORECOLOUR) { - col = charcode & (CON_2NDCHARSETTEXT|CON_RICHFORECOLOUR|(0xfff<alttint[0]; rgba[1] *= font->alttint[1]; @@ -2063,27 +2114,27 @@ float Font_DrawScaleChar(float px, float py, unsigned int charcode) } else { - col = charcode & (CON_2NDCHARSETTEXT|CON_NONCLEARBG|CON_BGMASK|CON_FGMASK|CON_HALFALPHA); + col = charflags & (CON_2NDCHARSETTEXT|CON_NONCLEARBG|CON_BGMASK|CON_FGMASK|CON_HALFALPHA); if (col != font_colourmask) { vec4_t rgba; - if (font_backcolour[3] != ((charcode & CON_NONCLEARBG)?127:0)) + if (font_backcolour[3] != ((charflags & CON_NONCLEARBG)?127:0)) Font_Flush(); font_colourmask = col; - col = (charcode&CON_FGMASK)>>CON_FGSHIFT; + col = (charflags&CON_FGMASK)>>CON_FGSHIFT; rgba[0] = consolecolours[col].fr*255; rgba[1] = consolecolours[col].fg*255; rgba[2] = consolecolours[col].fb*255; - rgba[3] = (charcode & CON_HALFALPHA)?127:255; + rgba[3] = (charflags & CON_HALFALPHA)?127:255; - col = (charcode&CON_BGMASK)>>CON_BGSHIFT; + col = (charflags&CON_BGMASK)>>CON_BGSHIFT; font_backcolour[0] = consolecolours[col].fr*255; font_backcolour[1] = consolecolours[col].fg*255; font_backcolour[2] = consolecolours[col].fb*255; - font_backcolour[3] = (charcode & CON_NONCLEARBG)?127:0; + font_backcolour[3] = (charflags & CON_NONCLEARBG)?127:0; - if (charcode & CON_2NDCHARSETTEXT) + if (charflags & CON_2NDCHARSETTEXT) { rgba[0] *= font->alttint[0]; rgba[1] *= font->alttint[1]; diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index 295baf974..2251e6965 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -125,7 +125,7 @@ static void Mod_MemList_f(void) #ifndef SERVERONLY static void Mod_BatchList_f(void) { - int m, i, lm; + int m, i; model_t *mod; batch_t *batch; unsigned int count; @@ -2441,7 +2441,7 @@ qboolean Mod_LoadFaces (model_t *loadmodel, qbyte *mod_base, lump_t *l, lump_t * out->firstedge = LittleLong(inl->firstedge); out->numedges = LittleLong(inl->numedges); tn = LittleLong (inl->texinfo); - for (i=0 ; istyles[i] = inl->styles[i]; lofs = LittleLong(inl->lightofs); inl++; @@ -2453,7 +2453,7 @@ qboolean Mod_LoadFaces (model_t *loadmodel, qbyte *mod_base, lump_t *l, lump_t * out->firstedge = LittleLong(ins->firstedge); out->numedges = LittleShort(ins->numedges); tn = LittleShort (ins->texinfo); - for (i=0 ; istyles[i] = ins->styles[i]; lofs = LittleLong(ins->lightofs); ins++; diff --git a/engine/gl/gl_rmain.c b/engine/gl/gl_rmain.c index 0509996fd..123275ed7 100644 --- a/engine/gl/gl_rmain.c +++ b/engine/gl/gl_rmain.c @@ -56,12 +56,13 @@ extern cvar_t gl_part_flame; extern cvar_t r_bloom; extern cvar_t r_wireframe_smooth; +cvar_t r_renderscale = CVARD("r_renderscale", "1", "Provides a way to enable subsampling or super-sampling"); cvar_t gl_affinemodels = SCVAR("gl_affinemodels","0"); cvar_t gl_finish = SCVAR("gl_finish","0"); cvar_t gl_dither = SCVAR("gl_dither", "1"); extern cvar_t r_stereo_separation; extern cvar_t r_stereo_method; -extern cvar_t r_postprocshader; +extern cvar_t r_postprocshader, r_fxaa; extern cvar_t gl_screenangle; @@ -87,6 +88,8 @@ cvar_t r_xflip = SCVAR("leftisright", "0"); extern cvar_t scr_fov; +shader_t *scenepp_rescaled; +shader_t *scenepp_antialias; shader_t *scenepp_waterwarp; // post processing stuff @@ -141,6 +144,23 @@ void GL_InitSceneProcessingShaders (void) GL_InitSceneProcessingShaders_WaterWarp(); } + scenepp_rescaled = R_RegisterShader("fte_rescaler", 0, + "{\n" + "program default2d\n" + "{\n" + "map $sourcecolour\n" + "}\n" + "}\n" + ); + scenepp_antialias = R_RegisterShader("fte_ppantialias", 0, + "{\n" + "program fxaa\n" + "{\n" + "map $sourcecolour\n" + "}\n" + "}\n" + ); + r_wireframe_smooth.modified = true; gl_dither.modified = true; //fixme: bad place for this, but hey vid_srgb.modified = true; @@ -1641,13 +1661,13 @@ texid_t R_RenderPostProcess (texid_t sourcetex, int type, shader_t *shader, char int h = (r_refdef.vrect.height * vid.pixelheight) / vid.height; sourcetex = R2D_RT_Configure(restexname, w, h, TF_RGBA32); GLBE_FBO_Update(&fbo_postproc, 0, &sourcetex, 1, r_nulltex, w, h, 0); - R2D_ScalePic(0, vid.pixelheight-r_refdef.vrect.height, r_refdef.vrect.width, r_refdef.vrect.height, scenepp_waterwarp); + R2D_ScalePic(0, vid.pixelheight-r_refdef.vrect.height, r_refdef.vrect.width, r_refdef.vrect.height, shader); GLBE_RenderToTextureUpdate2d(true); } else { //yay, dump it to the screen //update stuff now that we're not rendering the 3d scene - R2D_ScalePic(r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height, scenepp_waterwarp); + R2D_ScalePic(r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height, shader); } } @@ -1667,6 +1687,7 @@ void GLR_RenderView (void) double time1 = 0, time2; texid_t sourcetex = r_nulltex; shader_t *custompostproc = NULL; + float renderscale = r_renderscale.value; //extreme, but whatever checkglerror(); @@ -1721,6 +1742,9 @@ void GLR_RenderView (void) r_refdef.flags |= RDF_CUSTOMPOSTPROC; } + if (!(r_refdef.flags & RDF_NOWORLDMODEL) && r_fxaa.ival) //overlays will have problems. + r_refdef.flags |= RDF_ANTIALIAS; + //disable stuff if its simply not supported. if (dofbo || !gl_config.arb_shader_objects || !gl_config.ext_framebuffer_objects || !sh_config.texture_non_power_of_two_pic) r_refdef.flags &= ~(RDF_ALLPOSTPROC); //block all of this stuff @@ -1773,11 +1797,21 @@ void GLR_RenderView (void) flags |= FBO_RB_DEPTH; GLBE_FBO_Update(&fbo_gameview, flags, col, mrt, depth, vid.fbpwidth, vid.fbpheight, 0); } - else if (r_refdef.flags & (RDF_ALLPOSTPROC)) + else if ((r_refdef.flags & (RDF_ALLPOSTPROC)) || renderscale != 1) { //the game needs to be drawn to a texture for post processing - vid.fbvwidth = vid.fbpwidth = (r_refdef.vrect.width * vid.pixelwidth) / vid.width; - vid.fbvheight = vid.fbpheight = (r_refdef.vrect.height * vid.pixelheight) / vid.height; + vid.fbpwidth = (r_refdef.vrect.width * vid.pixelwidth) / vid.width; + vid.fbpheight = (r_refdef.vrect.height * vid.pixelheight) / vid.height; + + vid.fbpwidth *= renderscale; + vid.fbpheight *= renderscale; + + //well... err... meh. + vid.fbpwidth = bound(1, vid.fbpwidth, sh_config.texture_maxsize); + vid.fbpheight = bound(1, vid.fbpheight, sh_config.texture_maxsize); + + vid.fbvwidth = vid.fbpwidth; + vid.fbvheight = vid.fbpheight; sourcetex = R2D_RT_Configure("rt/$lastgameview", vid.fbpwidth, vid.fbpheight, /*(r_refdef.flags&RDF_BLOOM)?TF_RGBA16F:*/TF_RGBA32); @@ -1861,10 +1895,19 @@ void GLR_RenderView (void) // SCENE POST PROCESSING - sourcetex = R_RenderPostProcess (sourcetex, RDF_WATERWARP, scenepp_waterwarp, "rt/$waterwarped"); - sourcetex = R_RenderPostProcess (sourcetex, RDF_CUSTOMPOSTPROC, custompostproc, "rt/$postproced"); - if (r_refdef.flags & RDF_BLOOM) - R_BloomBlend(sourcetex, r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height); + if (renderscale != 1 && !(r_refdef.flags & RDF_ALLPOSTPROC)) + { + GLBE_FBO_Sources(sourcetex, r_nulltex); + R2D_Image(r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height, 0, 1, 1, 0, scenepp_rescaled); + } + else + { + sourcetex = R_RenderPostProcess (sourcetex, RDF_WATERWARP, scenepp_waterwarp, "rt/$waterwarped"); + sourcetex = R_RenderPostProcess (sourcetex, RDF_CUSTOMPOSTPROC, custompostproc, "rt/$postproced"); + sourcetex = R_RenderPostProcess (sourcetex, RDF_ANTIALIAS, scenepp_antialias, "rt/$antialiased"); + if (r_refdef.flags & RDF_BLOOM) + R_BloomBlend(sourcetex, r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height); + } GLBE_FBO_Sources(r_nulltex, r_nulltex); diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 39be13b30..15e4eeb8b 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -970,7 +970,9 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip "#define SKELETAL\n", "#define FOG\n", "#define FRAMEBLEND\n", +#if MAXRLIGHTMAPS > 1 "#define LIGHTSTYLED\n", +#endif NULL }; #define MAXMODIFIERS 64 @@ -1340,9 +1342,10 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip initoffset = VFS_GETLEN(blobfile); VFS_SEEK(blobfile, initoffset); } - if (!sh_config.pCreateProgram(prog, name, p, ver, permutationdefines, script, tess?script:NULL, tess?script:NULL, geom?script:NULL, script, (p & PERMUTATION_SKELETAL)?true:onefailed, sh_config.pValidateProgram?NULL:blobfile)) +#define SILENTPERMUTATIONS (developer.ival?0:PERMUTATION_SKELETAL) + if (!sh_config.pCreateProgram(prog, name, p, ver, permutationdefines, script, tess?script:NULL, tess?script:NULL, geom?script:NULL, script, (p & SILENTPERMUTATIONS)?true:onefailed, sh_config.pValidateProgram?NULL:blobfile)) { - if (!(p & PERMUTATION_SKELETAL)) + if (!(p & SILENTPERMUTATIONS)) onefailed = true; //don't flag it if skeletal failed. if (!p) //give up if permutation 0 failed. that one failing is fatal. break; @@ -1672,6 +1675,7 @@ struct shader_field_names_s shader_unif_names[] = {"l_shadowmapproj", SP_LIGHTSHADOWMAPPROJ}, {"l_shadowmapscale", SP_LIGHTSHADOWMAPSCALE}, + {"e_sourcesize", SP_SOURCESIZE}, {"e_rendertexturescale", SP_RENDERTEXTURESCALE}, {NULL} }; diff --git a/engine/gl/gl_shadow.c b/engine/gl/gl_shadow.c index 067c5519d..30d5ed47f 100644 --- a/engine/gl/gl_shadow.c +++ b/engine/gl/gl_shadow.c @@ -3421,6 +3421,9 @@ void Sh_CalcPointLight(vec3_t point, vec3_t light) if (!(dl->flags & ignoreflags)) continue; + if (dl->key == cl.playerview[0].viewentity) //ignore the light if its emitting from the player. generally the player can't *SEE* that light so it still counts. + continue; //disable this check if this function gets used for anything other than iris adaptation + colour[0] = dl->color[0]; colour[1] = dl->color[1]; colour[2] = dl->color[2]; diff --git a/engine/gl/gl_vidcommon.c b/engine/gl/gl_vidcommon.c index 7be3987c2..9ad8ba4b0 100644 --- a/engine/gl/gl_vidcommon.c +++ b/engine/gl/gl_vidcommon.c @@ -1139,12 +1139,12 @@ static const char *glsl_hdrs[] = "uniform sampler2D s_reflectmask;\n" "uniform sampler2D s_lightmap;\n" "uniform sampler2D s_deluxmap;\n" -#if MAXRLIGHTMAPS > 1 "#define s_lightmap0 s_lightmap\n" + "#define s_deluxmap0 s_deluxmap\n" +#if MAXRLIGHTMAPS > 1 "uniform sampler2D s_lightmap1;\n" "uniform sampler2D s_lightmap2;\n" "uniform sampler2D s_lightmap3;\n" - "#define s_deluxmap0 s_deluxmap\n" "uniform sampler2D s_deluxmap1;\n" "uniform sampler2D s_deluxmap2;\n" "uniform sampler2D s_deluxmap3;\n" @@ -1205,11 +1205,13 @@ static const char *glsl_hdrs[] = "uniform mat4 m_modelview;\n" "uniform mat4 m_projection;\n" // "uniform mat4 m_modelviewprojection;\n" + "#ifdef SKELETAL\n" //skeletal permutation tends to require glsl 120 #ifdef GLESONLY - "uniform vec4 m_bones[3*"STRINGIFY(MAX_GPU_BONES)"];\n" + "uniform vec4 m_bones[3*"STRINGIFY(MAX_GPU_BONES)"];\n" #else - "uniform mat4 m_bones["STRINGIFY(MAX_GPU_BONES)"];\n" + "uniform mat3x4 m_bones["STRINGIFY(MAX_GPU_BONES)"];\n" #endif + "#endif\n" "uniform mat4 m_invviewprojection;\n" "uniform mat4 m_invmodelviewprojection;\n" @@ -1920,6 +1922,7 @@ static GLhandleARB GLSlang_FinishShader(GLhandleARB shader, const char *name, GL char *typedesc; char str[8192]; + *str = 0; qglGetShaderInfoLog_(shader, sizeof(str), NULL, str); if (!silent) { diff --git a/engine/gl/shader.h b/engine/gl/shader.h index e8f6778a6..9bac911f3 100644 --- a/engine/gl/shader.h +++ b/engine/gl/shader.h @@ -324,8 +324,10 @@ enum{ PERMUTATION_FRAMEBLEND = 64, #if MAXRLIGHTMAPS > 1 PERMUTATION_LIGHTSTYLES = 128, //FIXME: make argument -#endif PERMUTATIONS = 256 +#else + PERMUTATIONS = 128 +#endif }; enum shaderattribs_e @@ -399,6 +401,7 @@ typedef struct { SP_M_INVMODELVIEWPROJECTION, SP_RENDERTEXTURESCALE, /*multiplier for currentrender->texcoord*/ + SP_SOURCESIZE, /*size of $sourcecolour*/ SP_LIGHTRADIUS, /*these light values are realtime lighting*/ SP_LIGHTCOLOUR, diff --git a/engine/server/progdefs.h b/engine/server/progdefs.h index a9855c014..727b7d523 100644 --- a/engine/server/progdefs.h +++ b/engine/server/progdefs.h @@ -206,7 +206,7 @@ and the extension fields are added on the end and can have extra vm-specific stu comfieldentity(tag_entity,NULL)\ comfieldfloat(tag_index,NULL)\ comfieldfloat(skeletonindex,"This object serves as a container for the skeletal bone states used to override the animation data.") /*FTE_CSQC_SKELETONOBJECTS*/\ - comfieldvector(colormod,NULL)\ + comfieldvector(colormod,"Provides a colour tint for the entity.")\ comfieldvector(glowmod,NULL)\ comfieldvector(gravitydir,"Specifies the direction in which gravity acts. Must be normalised. '0 0 0' also means down. Use '0 0 1' if you want the player to be able to run on ceilings.")\ comfieldfunction(camera_transform,".vector(vector org, vector ang)", "Provides portal transform information for portal surfaces attached to this entity. Also used to open up pvs in ssqc.")\ diff --git a/engine/shaders/glsl/bloom_final.glsl b/engine/shaders/glsl/bloom_final.glsl index 4ca9f7164..c19092750 100644 --- a/engine/shaders/glsl/bloom_final.glsl +++ b/engine/shaders/glsl/bloom_final.glsl @@ -1,4 +1,5 @@ !!cvarf r_bloom +!!cvarf r_bloom_retain=1.0 //add them together //optionally apply tonemapping @@ -18,10 +19,11 @@ uniform sampler2D s_t1; uniform sampler2D s_t2; uniform sampler2D s_t3; uniform float cvar_r_bloom; +uniform float cvar_r_bloom_retain; void main () { gl_FragColor = - texture2D(s_t0, tc) + + cvar_r_bloom_retain * texture2D(s_t0, tc) + cvar_r_bloom*( texture2D(s_t1, tc) + texture2D(s_t2, tc) + diff --git a/engine/shaders/glsl/fxaa.glsl b/engine/shaders/glsl/fxaa.glsl new file mode 100644 index 000000000..e0ea01a52 --- /dev/null +++ b/engine/shaders/glsl/fxaa.glsl @@ -0,0 +1,73 @@ +/* +This shader implements super-sampled anti-aliasing. +*/ + +varying vec2 texcoord; + +#ifdef VERTEX_SHADER +attribute vec2 v_texcoord; +void main() +{ + texcoord = v_texcoord.xy; + texcoord.y = 1.0 - texcoord.y; + gl_Position = ftetransform(); +} +#endif +#ifdef FRAGMENT_SHADER +uniform sampler2D s_t0; +uniform vec2 e_sourcesize; + +void main( void ) +{ + //gl_FragColor.xyz = texture2D(buf0,texcoord).xyz; + //return; + + float FXAA_SPAN_MAX = 8.0; + float FXAA_REDUCE_MUL = 1.0/8.0; + float FXAA_REDUCE_MIN = 1.0/128.0; + + vec3 rgbNW=texture2D(s_t0,texcoord+(vec2(-1.0,-1.0)/e_sourcesize)).xyz; + vec3 rgbNE=texture2D(s_t0,texcoord+(vec2(1.0,-1.0)/e_sourcesize)).xyz; + vec3 rgbSW=texture2D(s_t0,texcoord+(vec2(-1.0,1.0)/e_sourcesize)).xyz; + vec3 rgbSE=texture2D(s_t0,texcoord+(vec2(1.0,1.0)/e_sourcesize)).xyz; + vec3 rgbM=texture2D(s_t0,texcoord).xyz; + + vec3 luma=vec3(0.299, 0.587, 0.114); + float lumaNW = dot(rgbNW, luma); + float lumaNE = dot(rgbNE, luma); + float lumaSW = dot(rgbSW, luma); + float lumaSE = dot(rgbSE, luma); + float lumaM = dot(rgbM, luma); + + float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); + float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); + + vec2 dir; + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); + + float dirReduce = max( + (lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), + FXAA_REDUCE_MIN); + + float rcpDirMin = 1.0/(min(abs(dir.x), abs(dir.y)) + dirReduce); + + dir = min(vec2( FXAA_SPAN_MAX, FXAA_SPAN_MAX), + max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), + dir * rcpDirMin)) / e_sourcesize; + + vec3 rgbA = (1.0/2.0) * ( + texture2D(s_t0, texcoord.xy + dir * (1.0/3.0 - 0.5)).xyz + + texture2D(s_t0, texcoord.xy + dir * (2.0/3.0 - 0.5)).xyz); + vec3 rgbB = rgbA * (1.0/2.0) + (1.0/4.0) * ( + texture2D(s_t0, texcoord.xy + dir * (0.0/3.0 - 0.5)).xyz + + texture2D(s_t0, texcoord.xy + dir * (3.0/3.0 - 0.5)).xyz); + float lumaB = dot(rgbB, luma); + + if((lumaB < lumaMin) || (lumaB > lumaMax)){ + gl_FragColor.xyz=rgbA; + }else{ + gl_FragColor.xyz=rgbB; + } +} +#endif