diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c index e00f9c96..60093e00 100644 --- a/engine/client/cl_input.c +++ b/engine/client/cl_input.c @@ -1164,7 +1164,7 @@ float CL_FilterTime (double time, float wantfps, qboolean ignoreserver) //now re { if (!wantfps) return -1; - fps = max (30.0, wantfps); + fps = max (1.0, wantfps); } else { diff --git a/engine/client/client.h b/engine/client/client.h index a00375d1..ee608994 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -1341,24 +1341,25 @@ char *CG_GetConfigString(int num); // #ifdef CSQC_DAT qboolean CSQC_Inited(void); -void CSQC_RendererRestarted(void); +void CSQC_RendererRestarted(void); qboolean CSQC_UnconnectedOkay(qboolean inprinciple); qboolean CSQC_UnconnectedInit(void); qboolean CSQC_Init (qboolean anycsqc, qboolean csdatenabled, unsigned int checksum); qboolean CSQC_ConsoleLink(char *text, char *info); -void CSQC_RegisterCvarsAndThings(void); +void CSQC_RegisterCvarsAndThings(void); qboolean CSQC_SetupToRenderPortal(int entnum); qboolean CSQC_DrawView(void); qboolean CSQC_UseGamecodeLoadingScreen(void); -void CSQC_Shutdown(void); +void CSQC_Shutdown(void); qboolean CSQC_StuffCmd(int lplayernum, char *cmd, char *cmdend); +void CSQC_MapEntityEdited(int idx, const char *newe); qboolean CSQC_LoadResource(char *resname, char *restype); qboolean CSQC_ParsePrint(char *message, int printlevel); qboolean CSQC_ParseGamePacket(void); qboolean CSQC_CenterPrint(int lplayernum, char *cmd); qboolean CSQC_Parse_Damage(float save, float take, vec3_t source); -void CSQC_Input_Frame(int lplayernum, usercmd_t *cmd); -void CSQC_WorldLoaded(void); +void CSQC_Input_Frame(int lplayernum, usercmd_t *cmd); +void CSQC_WorldLoaded(void); qboolean CSQC_ParseTempEntity(void); qboolean CSQC_ConsoleCommand(char *cmd); qboolean CSQC_KeyPress(int key, int unicode, qboolean down, unsigned int devid); @@ -1367,16 +1368,16 @@ qboolean CSQC_MousePosition(float xabs, float yabs, unsigned int devid); qboolean CSQC_JoystickAxis(int axis, float value, unsigned int devid); qboolean CSQC_Accelerometer(float x, float y, float z); qboolean CSQC_Gyroscope(float x, float y, float z); -int CSQC_StartSound(int entnum, int channel, char *soundname, vec3_t pos, float vol, float attenuation, float pitchmod, float timeofs, unsigned int flags); -void CSQC_ParseEntities(void); -void CSQC_ResetTrails(void); +int CSQC_StartSound(int entnum, int channel, char *soundname, vec3_t pos, float vol, float attenuation, float pitchmod, float timeofs, unsigned int flags); +void CSQC_ParseEntities(void); +void CSQC_ResetTrails(void); qboolean CSQC_DeltaPlayer(int playernum, player_state_t *state); -void CSQC_DeltaStart(float time); +void CSQC_DeltaStart(float time); qboolean CSQC_DeltaUpdate(entity_state_t *src); -void CSQC_DeltaEnd(void); +void CSQC_DeltaEnd(void); -void CSQC_CvarChanged(cvar_t *var); +void CSQC_CvarChanged(cvar_t *var); #else #define CSQC_UnconnectedOkay(inprinciple) false #define CSQC_UnconnectedInit() false diff --git a/engine/client/console.c b/engine/client/console.c index bdc829de..3b957a87 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -73,6 +73,7 @@ cvar_t con_separatechat = CVAR("con_separatechat", "0"); cvar_t con_timestamps = CVAR("con_timestamps", "0"); cvar_t con_timeformat = CVAR("con_timeformat", "(%H:%M:%S) "); cvar_t con_textsize = CVARD("con_textsize", "8", "Resize the console text to be a different height, scaled separately from the hud. The value is the height in (virtual) pixels."); +extern cvar_t log_developer; #define NUM_CON_TIMES 24 @@ -1064,9 +1065,9 @@ void VARGS Con_SafeTPrintf (translation_t text, ...) static void Con_DPrintFromThread (void *ctx, void *data, size_t a, size_t b) { - if (!developer.value) + if (log_developer.ival) Con_Log(data); - else + if (developer.ival) { Sys_Printf ("%s", (const char*)data); // also echo to debugging console Con_PrintCon(&con_main, data, con_main.parseflags); @@ -1092,8 +1093,7 @@ void VARGS Con_DPrintf (const char *fmt, ...) Sys_Printf("%s", msg); return; #else - extern cvar_t log_developer; - if (!developer.value && !log_developer.value) + if (!developer.ival && !log_developer.ival) return; // early exit #endif @@ -1103,14 +1103,13 @@ void VARGS Con_DPrintf (const char *fmt, ...) if (!Sys_IsMainThread()) { - if (developer.ival) - COM_AddWork(WG_MAIN, Con_DPrintFromThread, NULL, Z_StrDup(msg), 0, 0); + COM_AddWork(WG_MAIN, Con_DPrintFromThread, NULL, Z_StrDup(msg), 0, 0); return; } - if (!developer.value) + if (log_developer.ival) Con_Log(msg); - else + if (developer.ival) { Sys_Printf ("%s", msg); // also echo to debugging console if (con_initialized) diff --git a/engine/client/m_download.c b/engine/client/m_download.c index 48c94f78..a620e855 100644 --- a/engine/client/m_download.c +++ b/engine/client/m_download.c @@ -1798,7 +1798,7 @@ static char *PM_GetTempName(package_t *p) return Z_StrDup(destname); } -static void PM_AddDownloadedPackage(const char *filename) +/*static void PM_AddDownloadedPackage(const char *filename) { char pathname[1024]; package_t *p; @@ -1822,7 +1822,7 @@ static void PM_AddDownloadedPackage(const char *filename) p->license = NULL; p->author = NULL; p->previewimage = NULL; -} +}*/ int PM_IsApplying(void) { @@ -2041,6 +2041,8 @@ static void PM_ApplyChanges(void) PM_StartADownload(); //and try to do those downloads. } +//names packages that were listed from the manifest. +//if 'mark' is true, then this is an initial install. void PM_ManifestPackage(const char *metaname, qboolean mark) { domanifestinstall = mark; @@ -2298,6 +2300,9 @@ void Menu_Download_Update(void) void PM_LoadPackages(searchpath_t **oldpaths, const char *parent_pure, const char *parent_logical, searchpath_t *search, unsigned int loadstuff, int minpri, int maxpri) { } +void PM_ManifestPackage(const char *metaname, qboolean mark) +{ +} void PM_Shutdown(void) { } diff --git a/engine/client/m_options.c b/engine/client/m_options.c index c5cd08f2..010fc77b 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -2832,6 +2832,7 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_ const char *fname; shader_t *shader; vec2_t fs = {8,8}; + float bones[12*MAX_BONES]; modelview_t *mods = c->dptr; @@ -2887,9 +2888,19 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_ ent.shaderTime = 0;//realtime; ent.framestate.g[FS_REG].lerpweight[0] = 1; ent.framestate.g[FS_REG].frame[0] = mods->framegroup; - ent.framestate.g[FS_REG].frametime[0] = realtime - mods->framechangetime; - ent.framestate.g[FS_REG].frametime[1] = realtime - mods->framechangetime; + ent.framestate.g[FS_REG].frametime[0] = ent.framestate.g[FS_REG].frametime[1] = realtime - mods->framechangetime; ent.customskin = Mod_RegisterSkinFile(va("%s_0.skin", mods->modelname)); + +// ent.framestate.bonecount = Mod_GetNumBones(ent.model, false); + ent.framestate.bonestate = bones; + ent.framestate.bonecount = Mod_GetBoneRelations(ent.model, 0, MAX_BONES, &ent.framestate, ent.framestate.bonestate); + ent.framestate.skeltype = SKEL_RELATIVE; + + ent.light_avg[0] = ent.light_avg[1] = ent.light_avg[2] = 0.66; + ent.light_range[0] = ent.light_range[1] = ent.light_range[2] = 0.33; + ent.light_dir[0] = 0; ent.light_dir[1] = 1; ent.light_dir[2] = 0; + ent.light_known = 2; + V_AddEntity(&ent); V_ApplyRefdef(); diff --git a/engine/client/merged.h b/engine/client/merged.h index 126b58f2..85ab5654 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -161,6 +161,7 @@ enum mlverbosity_e const char *Mod_GetEntitiesString(struct model_s *mod); void Mod_SetEntitiesStringLen(struct model_s *mod, const char *str, size_t strsize); void Mod_SetEntitiesString(struct model_s *mod, const char *str, qboolean docopy); +void Mod_ParseEntities(struct model_s *mod); extern void Mod_ClearAll (void); extern void Mod_Purge (enum mod_purge_e type); extern struct model_s *Mod_FindName (const char *name); //find without loading. needload should be set. diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index e2c85711..d4368ec4 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -126,6 +126,8 @@ extern sfx_t *cl_sfx_r_exp3; globalfunction(loadresource, "CSQC_LoadResource");/*EXT_CSQC_1*/ \ globalfunction(parse_tempentity, "CSQC_Parse_TempEntity");/*EXT_CSQC_ABSOLUTLY_VILE*/ \ \ + globalfunction(mapentityedited, "CSQC_MapEntityEdited");\ + \ /*These are pointers to the csqc's globals.*/ \ globalfloat(simtime, "time"); /*float The simulation(aka: smoothed server) time, speed drifts based upon latency*/ \ globalfloat(frametime, "frametime"); /*float Client render frame interval*/ \ @@ -1504,14 +1506,13 @@ void QCBUILTIN PF_R_AddTrisoup(pubprogfuncs_t *prinst, struct globalvars_s *pr_g unsigned int vertsptr = G_INT(OFS_PARM2); unsigned int indexesptr = G_INT(OFS_PARM3); unsigned int numindexes = G_INT(OFS_PARM4); - qboolean twod = qcflags & 4; + qboolean twod = qcflags & DRAWFLAG_2D; unsigned int beflags; unsigned int numverts; qcvertex_t *vert; unsigned int *idx; unsigned int i, j, first; - if ((qcflags & 3) == DRAWFLAG_ADD) beflags = BEF_NOSHADOWS|BEF_FORCEADDITIVE; else if ((qcflags & 3) == DRAWFLAG_MODULATE) @@ -1536,9 +1537,7 @@ void QCBUILTIN PF_R_AddTrisoup(pubprogfuncs_t *prinst, struct globalvars_s *pr_g //validates the pointer. numverts = (prinst->stringtablesize - vertsptr) / sizeof(qcvertex_t); - if (numverts < 1) - numverts = 1; - if (vertsptr <= 0 || vertsptr+numverts*sizeof(qcvertex_t) >= prinst->stringtablesize) + if (numverts < 1 || vertsptr <= 0 || vertsptr+numverts*sizeof(qcvertex_t) >= prinst->stringtablesize) { PR_BIError(prinst, "PF_R_AddTrisoup: invalid vertexes pointer\n"); return; @@ -7611,6 +7610,18 @@ qboolean CSQC_ParseGamePacket(void) return true; } +void CSQC_MapEntityEdited(int idx, const char *newe) +{ + void *pr_globals; + if (!csqcprogs || !csqcg.mapentityedited) + return; + + pr_globals = PR_globals(csqcprogs, PR_CURRENT); + G_INT(OFS_PARM0) = idx; + (((string_t *)pr_globals)[OFS_PARM1] = PR_TempString(csqcprogs, newe)); + PR_ExecuteProgram (csqcprogs, csqcg.mapentityedited); +} + qboolean CSQC_LoadResource(char *resname, char *restype) { void *pr_globals; diff --git a/engine/client/r_d3.c b/engine/client/r_d3.c index b16ec8f2..1e1d1e4d 100644 --- a/engine/client/r_d3.c +++ b/engine/client/r_d3.c @@ -4,13 +4,13 @@ #ifndef SERVERONLY #include "shader.h" #endif +#include "com_mesh.h" //FIXME: shadowmaps should build a cache of the nearby area surfaces and flag those models as RF_NOSHADOW or something //fixme: merge areas and static ents too somehow. void Mod_SetParent (mnode_t *node, mnode_t *parent); static int D3_ClusterForPoint (struct model_s *model, vec3_t point); -void R_Generate_Mesh_ST_Vectors(mesh_t *mesh); #ifndef SERVERONLY void ModD3_GenAreaVBO(void *ctx, void *data, size_t a, size_t b) diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index f56f7b77..ce25cd12 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -2347,6 +2347,7 @@ void Surf_SetupFrame(void) { mleaf_t *leaf; vec3_t temp, pvsorg; + int viewcontents; if (!cl.worldmodel || (!cl.worldmodel->nodes && cl.worldmodel->type != mod_heightmap)) r_refdef.flags |= RDF_NOWORLDMODEL; @@ -2364,7 +2365,7 @@ void Surf_SetupFrame(void) R_UpdateHDR(r_refdef.vieworg); } - r_viewcontents = 0; + viewcontents = 0; if (r_refdef.flags & RDF_NOWORLDMODEL) { } @@ -2385,7 +2386,7 @@ void Surf_SetupFrame(void) r_viewleaf2 = NULL; leaf = Mod_PointInLeaf (cl.worldmodel, pvsorg); - r_viewcontents = cl.worldmodel->funcs.PointContents(cl.worldmodel, NULL, pvsorg); + viewcontents = cl.worldmodel->funcs.PointContents(cl.worldmodel, NULL, pvsorg); r_viewcluster = r_viewcluster2 = leaf->cluster; // check above and below so crossing solid water doesn't draw wrong @@ -2449,22 +2450,22 @@ void Surf_SetupFrame(void) switch(r_viewleaf->contents) { case Q1CONTENTS_WATER: - r_viewcontents |= FTECONTENTS_WATER; + viewcontents |= FTECONTENTS_WATER; break; case Q1CONTENTS_LAVA: - r_viewcontents |= FTECONTENTS_LAVA; + viewcontents |= FTECONTENTS_LAVA; break; case Q1CONTENTS_SLIME: - r_viewcontents |= FTECONTENTS_SLIME; + viewcontents |= FTECONTENTS_SLIME; break; case Q1CONTENTS_SKY: - r_viewcontents |= FTECONTENTS_SKY; + viewcontents |= FTECONTENTS_SKY; break; case Q1CONTENTS_SOLID: - r_viewcontents |= FTECONTENTS_SOLID; + viewcontents |= FTECONTENTS_SOLID; break; case Q1CONTENTS_LADDER: - r_viewcontents |= FTECONTENTS_LADDER; + viewcontents |= FTECONTENTS_LADDER; break; } } @@ -2473,7 +2474,7 @@ void Surf_SetupFrame(void) #ifdef TERRAIN if (!(r_refdef.flags & RDF_NOWORLDMODEL) && cl.worldmodel && cl.worldmodel->terrain) { - r_viewcontents |= Heightmap_PointContents(cl.worldmodel, NULL, pvsorg); + viewcontents |= Heightmap_PointContents(cl.worldmodel, NULL, pvsorg); } #endif @@ -2484,12 +2485,16 @@ void Surf_SetupFrame(void) VectorCopy(pmove.player_maxs, t2); VectorClear(pmove.player_maxs); VectorClear(pmove.player_mins); - r_viewcontents |= PM_ExtraBoxContents(pvsorg); + viewcontents |= PM_ExtraBoxContents(pvsorg); VectorCopy(t1, pmove.player_mins); VectorCopy(t2, pmove.player_maxs); } - if (!r_secondaryview) - V_SetContentsColor (r_viewcontents); + if (!r_refdef.recurse) + { + r_viewcontents = viewcontents; + if (!r_secondaryview) + V_SetContentsColor (viewcontents); + } if (r_refdef.playerview->audio.defaulted) diff --git a/engine/client/renderer.c b/engine/client/renderer.c index 90d7f70f..d7356d84 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -101,6 +101,7 @@ cvar_t r_wireframe = CVARFD ("r_wireframe", "0", CVAR_CHEAT, "Developer feature where everything is drawn with wireframe over the top. Only active where cheats are permitted."); cvar_t r_wireframe_smooth = CVAR ("r_wireframe_smooth", "0"); cvar_t r_refract_fbo = CVARD ("r_refract_fbo", "1", "Use an fbo for refraction. If 0, just renders as a portal and uses a copy of the current framebuffer."); +cvar_t r_refractreflect_scale = CVARD ("r_refractreflect_scale", "0.5", "Use a different scale for refraction and reflection. Because $reasons."); cvar_t gl_miptexLevel = CVAR ("gl_miptexLevel", "0"); cvar_t r_drawviewmodel = CVARF ("r_drawviewmodel", "1", CVAR_ARCHIVE); cvar_t r_drawviewmodelinvis = CVAR ("r_drawviewmodelinvis", "0"); @@ -278,7 +279,7 @@ extern cvar_t r_drawentities; extern cvar_t r_drawviewmodel; extern cvar_t r_drawworld; extern cvar_t r_fullbright; -cvar_t r_mirroralpha = CVARFD("r_mirroralpha","1", CVAR_CHEAT|CVAR_SHADERSYSTEM, "Specifies how the default shader is generated for the 'window02_1' texture. Values less than 1 will turn it into a mirror."); +cvar_t r_mirroralpha = CVARFD("r_mirroralpha","1", CVAR_CHEAT|CVAR_SHADERSYSTEM|CVAR_RENDERERLATCH, "Specifies how the default shader is generated for the 'window02_1' texture. Values less than 1 will turn it into a mirror."); extern cvar_t r_netgraph; cvar_t r_norefresh = CVAR("r_norefresh","0"); extern cvar_t r_novis; @@ -798,6 +799,7 @@ void Renderer_Init(void) Cvar_Register (&r_wireframe, GRAPHICALNICETIES); Cvar_Register (&r_wireframe_smooth, GRAPHICALNICETIES); Cvar_Register (&r_refract_fbo, GRAPHICALNICETIES); + Cvar_Register (&r_refractreflect_scale, GRAPHICALNICETIES); Cvar_Register (&r_postprocshader, GRAPHICALNICETIES); Cvar_Register (&r_fxaa, GRAPHICALNICETIES); Cvar_Register (&r_renderscale, GRAPHICALNICETIES); diff --git a/engine/client/snd_directx.c b/engine/client/snd_directx.c index dea6d87f..f9bd76c5 100644 --- a/engine/client/snd_directx.c +++ b/engine/client/snd_directx.c @@ -20,27 +20,68 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "quakedef.h" #include "winquake.h" -#include -#ifndef DECLSPEC_SELECTANY -#define DECLSPEC_SELECTANY -#endif -#define FORCE_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ - EXTERN_C const GUID DECLSPEC_SELECTANY name \ - = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } - -#if _MSC_VER <= 1200 - DEFINE_GUID(IID_IKsPropertySet, 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa, 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93); - DEFINE_GUID(IID_IDirectSound, 0x279AFA83, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60); -#else - FORCE_DEFINE_GUID(IID_IDirectSound, 0x279AFA83, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60); - FORCE_DEFINE_GUID(IID_IKsPropertySet, 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa, 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93); -#endif - #ifdef AVAIL_DSOUND -#define iDirectSoundCreate(a,b,c) pDirectSoundCreate(a,b,c) -#define iDirectSoundEnumerate(a,b,c) pDirectSoundEnumerate(a,b) +//#define DIRECTSOUND_VERSION 0x800 //either < 0x800 (eax-only) or 0x800 (microsoft's fx stuff). +#include +#if !defined(IDirectSoundFXI3DL2Reverb_SetAllParameters) && DIRECTSOUND_VERSION>=0x800 + //mingw defines version as 0x900, but doesn't provide all the extra interfaces (only the core stuff). + //which makes it kinda pointless, so lets provide the crap that its missing. + typedef struct { + LONG lRoom; + LONG lRoomHF; + FLOAT flRoomRolloffFactor; + FLOAT flDecayTime; + FLOAT flDecayHFRatio; + LONG lReflections; + FLOAT flReflectionsDelay; + LONG lReverb; + FLOAT flReverbDelay; + FLOAT flDiffusion; + FLOAT flDensity; + FLOAT flHFReference; + } DSFXI3DL2Reverb; + + typedef struct IDirectSoundFXI3DL2Reverb + { + struct + { + STDMETHOD(QueryInterface)(struct IDirectSoundFXI3DL2Reverb *this_, REFIID riid, LPVOID * ppvObj); + STDMETHOD_(ULONG,AddRef)(struct IDirectSoundFXI3DL2Reverb *this_); + STDMETHOD_(ULONG,Release)(struct IDirectSoundFXI3DL2Reverb *this_); + STDMETHOD(SetAllParameters)(struct IDirectSoundFXI3DL2Reverb *this_, const DSFXI3DL2Reverb *pcDsFxI3DL2Reverb); + //INCOMPLETE + } *lpVtbl; + } IDirectSoundFXI3DL2Reverb; + #define IDirectSoundFXI3DL2Reverb8 IDirectSoundFXI3DL2Reverb + #define IID_IDirectSoundFXI3DL2Reverb8 IID_IDirectSoundFXI3DL2Reverb + + #define IDirectSoundFXI3DL2Reverb_Release(a) (a)->lpVtbl->Release(a) + #define IDirectSoundFXI3DL2Reverb_SetAllParameters(a,b) (a)->lpVtbl->SetAllParameters(a,b) +#endif + +#if _MSC_VER <= 1200 + #define FORCE_DEFINE_GUID DEFINE_GUID +#else + #ifndef DECLSPEC_SELECTANY + #define DECLSPEC_SELECTANY + #endif + #define FORCE_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + EXTERN_C const GUID DECLSPEC_SELECTANY name \ + = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } +#endif + +#if DIRECTSOUND_VERSION >= 0x0800 +FORCE_DEFINE_GUID(IID_IDirectSoundBuffer8, 0x6825a449, 0x7524, 0x4d82, 0x92, 0x0f, 0x50, 0xe3, 0x6a, 0xb3, 0xab, 0x1e); +FORCE_DEFINE_GUID(IID_IDirectSound8, 0xC50A7E93, 0xF395, 0x4834, 0x9E, 0xF6, 0x7F, 0xA9, 0x9D, 0xE5, 0x09, 0x66); +FORCE_DEFINE_GUID(IID_IDirectSoundFXI3DL2Reverb, 0x4b166a6a, 0x0d66, 0x43f3, 0x80, 0xe3, 0xee, 0x62, 0x80, 0xde, 0xe1, 0xa4); +FORCE_DEFINE_GUID(GUID_DSFX_STANDARD_I3DL2REVERB, 0xef985e71, 0xd5c7, 0x42d4, 0xba, 0x4d, 0x2d, 0x07, 0x3e, 0x2e, 0x96, 0xf4); +HRESULT (WINAPI *pDirectSoundCreate8)(GUID FAR *lpGUID, LPDIRECTSOUND8 FAR *lplpDS, IUnknown FAR *pUnkOuter); +#endif + +FORCE_DEFINE_GUID(IID_IDirectSound, 0x279AFA83, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60); +FORCE_DEFINE_GUID(IID_IKsPropertySet, 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa, 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93); HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter); HRESULT (WINAPI *pDirectSoundEnumerate)(LPDSENUMCALLBACKA lpCallback, LPVOID lpContext); #if defined(VOICECHAT) @@ -59,9 +100,19 @@ typedef struct { LPDIRECTSOUNDBUFFER pDSBuf; LPDIRECTSOUNDBUFFER pDSPBuf; +#if DIRECTSOUND_VERSION >= 0x0800 + //dsound8 interfaces, for reverb effects + LPDIRECTSOUND8 pDS8; + LPDIRECTSOUNDBUFFER8 pDSBuf8; + IDirectSoundFXI3DL2Reverb8 *pReverb; +#endif + DWORD gSndBufSize; DWORD mmstarttime; + int curreverb; + int curreverbmodcount; //so it updates if the effect itself is updated + #ifdef _IKsPropertySet_ LPKSPROPERTYSET EaxKsPropertiesSet; #endif @@ -73,14 +124,14 @@ static void DSOUND_Restore(soundcardinfo_t *sc) { DWORD dwStatus; dshandle_t *dh = sc->handle; - if (dh->pDSBuf->lpVtbl->GetStatus (dh->pDSBuf, &dwStatus) != ERROR_SUCCESS) + if (IDirectSoundBuffer_GetStatus (dh->pDSBuf, &dwStatus) != ERROR_SUCCESS) Con_Printf ("Couldn't get sound buffer status\n"); if (dwStatus & DSBSTATUS_BUFFERLOST) - dh->pDSBuf->lpVtbl->Restore (dh->pDSBuf); + IDirectSoundBuffer_Restore (dh->pDSBuf); if (!(dwStatus & DSBSTATUS_PLAYING)) - dh->pDSBuf->lpVtbl->Play(dh->pDSBuf, 0, 0, DSBPLAY_LOOPING); + IDirectSoundBuffer_Play(dh->pDSBuf, 0, 0, DSBPLAY_LOOPING); } static DWORD dsound_locksize; @@ -97,7 +148,7 @@ static void *DSOUND_Lock(soundcardinfo_t *sc, unsigned int *sampidx) reps = 0; - while ((hresult = dh->pDSBuf->lpVtbl->Lock(dh->pDSBuf, 0, dh->gSndBufSize, (void**)&ret, &dsound_locksize, + while ((hresult = IDirectSoundBuffer_Lock(dh->pDSBuf, 0, dh->gSndBufSize, (void**)&ret, &dsound_locksize, (void**)&pbuf2, &dwSize2, 0)) != DS_OK) { if (hresult != DSERR_BUFFERLOST) @@ -122,7 +173,7 @@ static void *DSOUND_Lock(soundcardinfo_t *sc, unsigned int *sampidx) static void DSOUND_Unlock(soundcardinfo_t *sc, void *buffer) { dshandle_t *dh = sc->handle; - dh->pDSBuf->lpVtbl->Unlock(dh->pDSBuf, buffer, dsound_locksize, NULL, 0); + IDirectSoundBuffer_Unlock(dh->pDSBuf, buffer, dsound_locksize, NULL, 0); } /* @@ -140,34 +191,39 @@ static void DSOUND_Shutdown_Internal (soundcardinfo_t *sc) sc->handle = NULL; #ifdef _IKsPropertySet_ if (dh->EaxKsPropertiesSet) - { IKsPropertySet_Release(dh->EaxKsPropertiesSet); - } + dh->EaxKsPropertiesSet = NULL; #endif + +#if DIRECTSOUND_VERSION >= 0x0800 + if (dh->pReverb) + IDirectSoundFXI3DL2Reverb_Release(dh->pReverb); + dh->pReverb = NULL; + if (dh->pDSBuf8) + IDirectSoundBuffer8_Release(dh->pDSBuf8); + dh->pDSBuf8 = NULL; +#endif + if (dh->pDSBuf) { - dh->pDSBuf->lpVtbl->Stop(dh->pDSBuf); - dh->pDSBuf->lpVtbl->Release(dh->pDSBuf); + IDirectSoundBuffer_Stop(dh->pDSBuf); + IDirectSoundBuffer_Release(dh->pDSBuf); + if (dh->pDSBuf == dh->pDSPBuf) + dh->pDSPBuf = NULL; + dh->pDSBuf = NULL; } -// only release primary buffer if it's not also the mixing buffer we just released - if (dh->pDSPBuf && (dh->pDSBuf != dh->pDSPBuf)) - { - dh->pDSPBuf->lpVtbl->Release(dh->pDSPBuf); - } + if (dh->pDSPBuf) + IDirectSoundBuffer_Release(dh->pDSPBuf); + dh->pDSPBuf = NULL; if (dh->pDS) { - dh->pDS->lpVtbl->SetCooperativeLevel (dh->pDS, mainwindow, DSSCL_NORMAL); - dh->pDS->lpVtbl->Release(dh->pDS); + IDirectSound_SetCooperativeLevel (dh->pDS, mainwindow, DSSCL_NORMAL); + IDirectSound_Release(dh->pDS); } dh->pDS = NULL; - dh->pDSBuf = NULL; - dh->pDSPBuf = NULL; -#ifdef _IKsPropertySet_ - dh->EaxKsPropertiesSet = NULL; -#endif Z_Free(dh); } @@ -225,7 +281,9 @@ static void DSOUND_Shutdown (soundcardinfo_t *sc) # define SPEAKER_TOP_BACK_RIGHT 0x20000 /* Bit mask locations reserved for future use*/ +#ifndef SPEAKER_RESERVED # define SPEAKER_RESERVED 0x7FFC0000 +#endif /* Used to specify that any possible permutation of speaker configurations*/ # define SPEAKER_ALL 0x80000000 @@ -391,11 +449,24 @@ typedef enum } DSPROPERTY_EAX_BUFFERPROPERTY; #endif -static void DSOUND_SetReverb(soundcardinfo_t *sc, size_t reverb) +static long GainToMillibels(float gain) +{ + return 100*20*(0.43429*log(gain)); +} +static void DSOUND_SetReverb(soundcardinfo_t *sc, size_t reverbidx) { -#ifdef _IKsPropertySet_ dshandle_t *dh = sc->handle; + struct reverbproperties_s *prop; + if (reverbidx >= numreverbproperties) + return; //invalid + if (dh->curreverb == reverbidx && dh->curreverbmodcount == reverbproperties[reverbidx].modificationcount) + return; //nothing changed. + dh->curreverb = reverbidx; + dh->curreverbmodcount = reverbproperties[reverbidx].modificationcount; + prop = &reverbproperties[dh->curreverb].props; + +#ifdef _IKsPropertySet_ //attempt at eax support. //EAX is a global thing. Get it going in a game and your media player will be doing it too. @@ -403,64 +474,20 @@ static void DSOUND_SetReverb(soundcardinfo_t *sc, size_t reverb) { EAXLISTENERPROPERTIES ListenerProperties = {0}; -/* DWORD p; - IKsPropertySet_Get(dh->EaxKsPropertiesSet, &DSPROPSETID_EAX20_LISTENERPROPERTIES, - DSPROPERTY_EAXLISTENER_ALLPARAMETERS, 0, 0, &ListenerProperties, - sizeof(ListenerProperties), &p); -*/ - if (reverb) - { -#if 1 //phycotic. - ListenerProperties.flEnvironmentSize = 2.8; - ListenerProperties.flEnvironmentDiffusion = 0.240; - ListenerProperties.lRoom = -374; - ListenerProperties.lRoomHF = -150; - ListenerProperties.flRoomRolloffFactor = 0; - ListenerProperties.flAirAbsorptionHF = -5; - ListenerProperties.lReflections = -10000; - ListenerProperties.flReflectionsDelay = 0.053; - ListenerProperties.lReverb = 625; - ListenerProperties.flReverbDelay = 0.08; - ListenerProperties.flDecayTime = 5.096; - ListenerProperties.flDecayHFRatio = 0.910; - ListenerProperties.dwFlags = 0x3f; - ListenerProperties.dwEnvironment = EAX_ENVIRONMENT_PSYCHOTIC; -#else - ListenerProperties.flEnvironmentSize = 5.8; - ListenerProperties.flEnvironmentDiffusion = 0; - ListenerProperties.lRoom = -374; - ListenerProperties.lRoomHF = -2860; - ListenerProperties.flRoomRolloffFactor = 0; - ListenerProperties.flAirAbsorptionHF = -5; - ListenerProperties.lReflections = -889; - ListenerProperties.flReflectionsDelay = 0.024; - ListenerProperties.lReverb = 797; - ListenerProperties.flReverbDelay = 0.035; - ListenerProperties.flDecayTime = 5.568; - ListenerProperties.flDecayHFRatio = 0.100; - ListenerProperties.dwFlags = 0x3f; - ListenerProperties.dwEnvironment = EAX_ENVIRONMENT_UNDERWATER; -#endif - } - else - { - ListenerProperties.flEnvironmentSize = 1; - ListenerProperties.flEnvironmentDiffusion = 0; - ListenerProperties.lRoom = 0; - ListenerProperties.lRoomHF = 0; - ListenerProperties.flRoomRolloffFactor = 0; - ListenerProperties.flAirAbsorptionHF = 0; - ListenerProperties.lReflections = 1000; - ListenerProperties.flReflectionsDelay = 0; - ListenerProperties.lReverb = 813; - ListenerProperties.flReverbDelay = 0.00; - ListenerProperties.flDecayTime = 0.1; - ListenerProperties.flDecayHFRatio = 0.1; - ListenerProperties.dwFlags = 0x3f; - ListenerProperties.dwEnvironment = EAX_ENVIRONMENT_GENERIC; - } - -// env = EAX_ENVIRONMENT_UNDERWATER; + ListenerProperties.flEnvironmentSize = prop->flEchoTime; + ListenerProperties.flEnvironmentDiffusion = prop->flDiffusion; + ListenerProperties.lRoom = GainToMillibels(prop->flGain); + ListenerProperties.lRoomHF = GainToMillibels(prop->flGainHF); + ListenerProperties.flRoomRolloffFactor = prop->flRoomRolloffFactor; + ListenerProperties.flAirAbsorptionHF = prop->flAirAbsorptionGainHF; + ListenerProperties.lReflections = GainToMillibels(prop->flReflectionsGain); + ListenerProperties.flReflectionsDelay = prop->flReflectionsDelay; + ListenerProperties.lReverb = GainToMillibels(prop->flLateReverbGain); + ListenerProperties.flReverbDelay = prop->flLateReverbDelay; + ListenerProperties.flDecayTime = prop->flDecayTime; + ListenerProperties.flDecayHFRatio = prop->flDecayHFRatio; + ListenerProperties.dwFlags = 0x3f; + ListenerProperties.dwEnvironment = reverbidx?EAX_ENVIRONMENT_UNDERWATER:0; if (FAILED(IKsPropertySet_Set(dh->EaxKsPropertiesSet, &DSPROPSETID_EAX20_LISTENERPROPERTIES, DSPROPERTY_EAXLISTENER_ALLPARAMETERS, 0, 0, &ListenerProperties, @@ -468,6 +495,27 @@ static void DSOUND_SetReverb(soundcardinfo_t *sc, size_t reverb) Con_SafePrintf ("EAX set failed\n"); } #endif + +#if DIRECTSOUND_VERSION >= 0x0800 + if (dh->pReverb) + { + DSFXI3DL2Reverb reverb; + reverb.lRoom = bound(-10000, GainToMillibels(prop->flGain), 0); // [-10000, 0] default: -1000 mB + reverb.lRoomHF = bound(-10000, GainToMillibels(prop->flGainHF), 0); // [-10000, 0] default: 0 mB + reverb.flRoomRolloffFactor = bound(0.0, prop->flRoomRolloffFactor, 10.0); // [0.0, 10.0] default: 0.0 + reverb.flDecayTime = bound(0.1, prop->flDecayTime, 20.0); // [0.1, 20.0] default: 1.49s + reverb.flDecayHFRatio = bound(0.1, prop->flDecayHFRatio, 2.0); // [0.1, 2.0] default: 0.83 + reverb.lReflections = bound(-10000, GainToMillibels(prop->flReflectionsGain), 1000); // [-10000, 1000] default: -2602 mB + reverb.flReflectionsDelay = bound(0.0, prop->flReflectionsDelay, 0.3); // [0.0, 0.3] default: 0.007 s + reverb.lReverb = bound(-10000, GainToMillibels(prop->flLateReverbGain), 2000); // [-10000, 2000] default: 200 mB + reverb.flReverbDelay = bound(0.0, prop->flLateReverbDelay, 0.1); // [0.0, 0.1] default: 0.011 s + reverb.flDiffusion = bound(0.0, prop->flDiffusion*100, 100.0); // [0.0, 100.0] default: 100.0 % + reverb.flDensity = bound(0.0, prop->flDensity*100, 100.0); // [0.0, 100.0] default: 100.0 % + reverb.flHFReference = bound(20.0, prop->flHFReference, 20000.0); // [20.0, 20000.0] default: 5000.0 Hz + + IDirectSoundFXI3DL2Reverb_SetAllParameters(dh->pReverb, &reverb); + } +#endif } /* @@ -487,7 +535,7 @@ static unsigned int DSOUND_GetDMAPos(soundcardinfo_t *sc) dshandle_t *dh = sc->handle; - dh->pDSBuf->lpVtbl->GetCurrentPosition(dh->pDSBuf, &mmtime, &dwWrite); + IDirectSoundBuffer_GetCurrentPosition(dh->pDSBuf, &mmtime, &dwWrite); s = mmtime - dh->mmstarttime; @@ -520,10 +568,17 @@ static qboolean DSOUND_InitOutputLibrary(void) } if (!pDirectSoundCreate) pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate"); - if (!pDirectSoundCreate) +#if DIRECTSOUND_VERSION >= 0x0800 + if (!pDirectSoundCreate8) + pDirectSoundCreate8 = (void *)GetProcAddress(hInstDS,"DirectSoundCreate8"); //xp+ + if (!pDirectSoundCreate8) +#endif { - Con_SafePrintf ("Couldn't get DS proc addr\n"); - return false; + if (!pDirectSoundCreate) + { + Con_SafePrintf ("Couldn't get DS proc addr\n"); + return false; + } } if (!pDirectSoundEnumerate) pDirectSoundEnumerate = (void *)GetProcAddress(hInstDS,"DirectSoundEnumerateA"); @@ -539,11 +594,8 @@ Direct-Sound support static int DSOUND_InitCard_Internal (soundcardinfo_t *sc, char *cardname) { extern cvar_t snd_inactive; -#if _MSC_VER > 1200 //fixme err -#ifdef _IKsPropertySet_ extern cvar_t snd_eax; -#endif -#endif + int usereverb; //2=eax, 1=ds8 DSBUFFERDESC dsbuf; DSBCAPS dsbcaps; DWORD dwSize, dwWrite; @@ -613,23 +665,23 @@ static int DSOUND_InitCard_Internal (soundcardinfo_t *sc, char *cardname) } format.Format.nChannels = sc->sn.numchannels; - format.Format.wBitsPerSample = sc->sn.samplebits; - format.Format.nSamplesPerSec = sc->sn.speed; - format.Format.nBlockAlign = format.Format.nChannels - *format.Format.wBitsPerSample / 8; - format.Format.nAvgBytesPerSec = format.Format.nSamplesPerSec - *format.Format.nBlockAlign; + format.Format.wBitsPerSample = sc->sn.samplebits; + format.Format.nSamplesPerSec = sc->sn.speed; + format.Format.nBlockAlign = format.Format.nChannels * format.Format.wBitsPerSample / 8; + format.Format.nAvgBytesPerSec = format.Format.nSamplesPerSec * format.Format.nBlockAlign; if (!DSOUND_InitOutputLibrary()) return false; sc->handle = Z_Malloc(sizeof(dshandle_t)); dh = sc->handle; + + usereverb = !!snd_eax.ival; //EAX attempt #if _MSC_VER > 1200 #ifdef _IKsPropertySet_ dh->pDS = NULL; - if (snd_eax.ival) + if (usereverb) { CoInitialize(NULL); if (FAILED(CoCreateInstance( &CLSID_EAXDirectSound, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectSound, (void **)&dh->pDS ))) @@ -637,6 +689,7 @@ static int DSOUND_InitCard_Internal (soundcardinfo_t *sc, char *cardname) else { IDirectSound_Initialize(dh->pDS, dsguid); + usereverb = 2; } } @@ -644,8 +697,22 @@ static int DSOUND_InitCard_Internal (soundcardinfo_t *sc, char *cardname) #endif #endif { - while ((hresult = iDirectSoundCreate(dsguid, &dh->pDS, NULL)) != DS_OK) + for(;;) { + dh->pDS = NULL; +#if DIRECTSOUND_VERSION >= 0x0800 + dh->pDS8 = NULL; + if (pDirectSoundCreate8) + { + hresult = pDirectSoundCreate8(dsguid, &dh->pDS8, NULL); + dh->pDS = (void*)dh->pDS8; //evil cast + } + else +#endif + hresult = pDirectSoundCreate(dsguid, &dh->pDS, NULL); + if (hresult == DS_OK) + break; + if (hresult != DSERR_ALLOCATED) { Con_SafePrintf (": create failed\n"); @@ -669,7 +736,7 @@ static int DSOUND_InitCard_Internal (soundcardinfo_t *sc, char *cardname) #ifdef FTE_SDL #define mainwindow GetDesktopWindow() #endif - if (DS_OK != dh->pDS->lpVtbl->SetCooperativeLevel (dh->pDS, mainwindow, DSSCL_EXCLUSIVE)) + if (DS_OK != IDirectSound_SetCooperativeLevel (dh->pDS, mainwindow, DSSCL_EXCLUSIVE)) { Con_SafePrintf ("Set coop level failed\n"); DSOUND_Shutdown_Internal (sc); @@ -678,7 +745,7 @@ static int DSOUND_InitCard_Internal (soundcardinfo_t *sc, char *cardname) dscaps.dwSize = sizeof(dscaps); - if (DS_OK != dh->pDS->lpVtbl->GetCaps (dh->pDS, &dscaps)) + if (DS_OK != IDirectSound_GetCaps (dh->pDS, &dscaps)) { Con_SafePrintf ("Couldn't get DS caps\n"); } @@ -716,11 +783,11 @@ static int DSOUND_InitCard_Internal (soundcardinfo_t *sc, char *cardname) if (!COM_CheckParm ("-snoforceformat")) { - if (DS_OK == dh->pDS->lpVtbl->CreateSoundBuffer(dh->pDS, &dsbuf, &dh->pDSPBuf, NULL)) + if (DS_OK == IDirectSound_CreateSoundBuffer(dh->pDS, &dsbuf, &dh->pDSPBuf, NULL)) { pformat = format; - if (DS_OK != dh->pDSPBuf->lpVtbl->SetFormat (dh->pDSPBuf, (WAVEFORMATEX *)&pformat)) + if (DS_OK != IDirectSoundBuffer_SetFormat (dh->pDSPBuf, (WAVEFORMATEX *)&pformat)) { // if (snd_firsttime) // Con_SafePrintf ("Set primary sound buffer format: no\n"); @@ -741,6 +808,12 @@ static int DSOUND_InitCard_Internal (soundcardinfo_t *sc, char *cardname) memset (&dsbuf, 0, sizeof(dsbuf)); dsbuf.dwSize = sizeof(DSBUFFERDESC); dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY|DSBCAPS_LOCSOFTWARE; //dmw 29 may, 2003 removed locsoftware + +#if DIRECTSOUND_VERSION >= 0x0800 + if (usereverb == 1) + dsbuf.dwFlags |= DSBCAPS_CTRLFX; +#endif + #ifdef DSBCAPS_GLOBALFOCUS if (snd_inactive.ival) { @@ -761,7 +834,7 @@ static int DSOUND_InitCard_Internal (soundcardinfo_t *sc, char *cardname) memset(&dsbcaps, 0, sizeof(dsbcaps)); dsbcaps.dwSize = sizeof(dsbcaps); - if (DS_OK != dh->pDS->lpVtbl->CreateSoundBuffer(dh->pDS, &dsbuf, &dh->pDSBuf, NULL)) + if (DS_OK != IDirectSound_CreateSoundBuffer(dh->pDS, &dsbuf, &dh->pDSBuf, NULL)) { Con_SafePrintf ("DS:CreateSoundBuffer Failed"); DSOUND_Shutdown_Internal (sc); @@ -772,7 +845,7 @@ static int DSOUND_InitCard_Internal (soundcardinfo_t *sc, char *cardname) sc->sn.samplebits = format.Format.wBitsPerSample; sc->sn.speed = format.Format.nSamplesPerSec; - if (DS_OK != dh->pDSBuf->lpVtbl->GetCaps (dh->pDSBuf, &dsbcaps)) + if (DS_OK != IDirectSoundBuffer_GetCaps (dh->pDSBuf, &dsbcaps)) { Con_SafePrintf ("DS:GetCaps failed\n"); DSOUND_Shutdown_Internal (sc); @@ -784,14 +857,14 @@ static int DSOUND_InitCard_Internal (soundcardinfo_t *sc, char *cardname) } else { - if (DS_OK != dh->pDS->lpVtbl->SetCooperativeLevel (dh->pDS, mainwindow, DSSCL_WRITEPRIMARY)) + if (DS_OK != IDirectSound_SetCooperativeLevel (dh->pDS, mainwindow, DSSCL_WRITEPRIMARY)) { Con_SafePrintf ("Set coop level failed\n"); DSOUND_Shutdown_Internal (sc); return false; } - if (DS_OK != dh->pDSPBuf->lpVtbl->GetCaps (dh->pDSPBuf, &dsbcaps)) + if (DS_OK != IDirectSoundBuffer_GetCaps (dh->pDSPBuf, &dsbcaps)) { Con_Printf ("DS:GetCaps failed\n"); DSOUND_Shutdown_Internal (sc); @@ -804,21 +877,38 @@ static int DSOUND_InitCard_Internal (soundcardinfo_t *sc, char *cardname) dh->gSndBufSize = dsbcaps.dwBufferBytes; +#if DIRECTSOUND_VERSION >= 0x0800 + if (usereverb == 1) + { + if (SUCCEEDED(IDirectSoundBuffer_QueryInterface(dh->pDSBuf, &IID_IDirectSoundBuffer8, (void*)&dh->pDSBuf8))) + { + DSEFFECTDESC effects[1]; + DWORD results[1]; + memset(effects, 0, sizeof(effects)); + effects[0].dwSize = sizeof(effects[0]); + effects[0].dwFlags = 0; + effects[0].guidDSFXClass = GUID_DSFX_STANDARD_I3DL2REVERB; + if (SUCCEEDED(IDirectSoundBuffer8_SetFX(dh->pDSBuf8, 1, effects, results))) + if (SUCCEEDED(IDirectSoundBuffer8_GetObjectInPath(dh->pDSBuf8, &GUID_DSFX_STANDARD_I3DL2REVERB, 0, &IID_IDirectSoundFXI3DL2Reverb8, (void*)&dh->pReverb))) + usereverb = 0; + } + } +#endif + #if 1 // Make sure mixer is active - dh->pDSBuf->lpVtbl->Play(dh->pDSBuf, 0, 0, DSBPLAY_LOOPING); + IDirectSoundBuffer_Play(dh->pDSBuf, 0, 0, DSBPLAY_LOOPING); -/* if (snd_firsttime) - Con_SafePrintf(" %d channel(s)\n" - " %d bits/sample\n" - " %d bytes/sec\n", - shm->channels, shm->samplebits, shm->speed);*/ + Con_DPrintf(" %d channel(s)\n" + " %d bits/sample\n" + " %d bytes/sec\n", + sc->sn.numchannels, sc->sn.samplebits, sc->sn.speed); // initialize the buffer reps = 0; - while ((hresult = dh->pDSBuf->lpVtbl->Lock(dh->pDSBuf, 0, dh->gSndBufSize, (void**)&buffer, &dwSize, NULL, NULL, 0)) != DS_OK) + while ((hresult = IDirectSoundBuffer_Lock(dh->pDSBuf, 0, dh->gSndBufSize, (void**)&buffer, &dwSize, NULL, NULL, 0)) != DS_OK) { if (hresult != DSERR_BUFFERLOST) { @@ -840,18 +930,21 @@ static int DSOUND_InitCard_Internal (soundcardinfo_t *sc, char *cardname) // Sleep(500); - dh->pDSBuf->lpVtbl->Unlock(dh->pDSBuf, buffer, dwSize, NULL, 0); + IDirectSoundBuffer_Unlock(dh->pDSBuf, buffer, dwSize, NULL, 0); - dh->pDSBuf->lpVtbl->Stop(dh->pDSBuf); + IDirectSoundBuffer_Stop(dh->pDSBuf); #endif - dh->pDSBuf->lpVtbl->GetCurrentPosition(dh->pDSBuf, &dh->mmstarttime, &dwWrite); - dh->pDSBuf->lpVtbl->Play(dh->pDSBuf, 0, 0, DSBPLAY_LOOPING); + IDirectSoundBuffer_GetCurrentPosition(dh->pDSBuf, &dh->mmstarttime, &dwWrite); + IDirectSoundBuffer_Play(dh->pDSBuf, 0, 0, DSBPLAY_LOOPING); sc->sn.samples = dh->gSndBufSize/(sc->sn.samplebits/8); sc->sn.samplepos = 0; sc->sn.buffer = NULL; + dh->curreverb = ~0; + dh->curreverbmodcount = ~0; + sc->Lock = DSOUND_Lock; sc->Unlock = DSOUND_Unlock; @@ -864,7 +957,7 @@ static int DSOUND_InitCard_Internal (soundcardinfo_t *sc, char *cardname) #if _MSC_VER > 1200 #ifdef _IKsPropertySet_ //attempt at eax support - if (snd_eax.ival) + if (usereverb == 2) { int r; DWORD support; @@ -878,20 +971,23 @@ static int DSOUND_InitCard_Internal (soundcardinfo_t *sc, char *cardname) IKsPropertySet_Release(dh->EaxKsPropertiesSet); dh->EaxKsPropertiesSet = NULL; Con_SafePrintf ("EAX 2 not supported\n"); - return true;//otherwise successful. It can be used for normal sound anyway. + //otherwise successful. It can be used for normal sound anyway. } - - //worked. EAX is supported. + else + usereverb = 0; //worked. EAX is fully inited. } else { - Con_SafePrintf ("Couldn't get extended properties\n"); + Con_DPrintf ("Couldn't get extended properties\n"); dh->EaxKsPropertiesSet = NULL; } } #endif #endif + if (usereverb) + Con_SafePrintf ("Couldn't enable environmental reverb effects\n"); + return true; } diff --git a/engine/client/snd_dma.c b/engine/client/snd_dma.c index 0b216c95..2a7272de 100644 --- a/engine/client/snd_dma.c +++ b/engine/client/snd_dma.c @@ -2373,7 +2373,7 @@ static void SND_AccumulateSpacialization(soundcardinfo_t *sc, channel_t *ch, vec dist = VectorNormalize(world_vec) * ch->dist_mult; - if (ch->flags & CF_NOSPACIALISE) + if ((ch->flags & CF_NOSPACIALISE) || !ch->dist_mult) { scale = 1; scale = (1.0 - dist) * scale; @@ -2501,7 +2501,7 @@ static void SND_Spatialize(soundcardinfo_t *sc, channel_t *ch) dist = VectorNormalize(world_vec) * ch->dist_mult; - if (ch->flags & CF_NOSPACIALISE) + if ((ch->flags & CF_NOSPACIALISE) || !ch->dist_mult) { scale = 1; scale = (1.0 - dist) * scale; diff --git a/engine/client/snd_xaudio.c b/engine/client/snd_xaudio.c index 30b12495..d92b71b8 100644 --- a/engine/client/snd_xaudio.c +++ b/engine/client/snd_xaudio.c @@ -2,7 +2,9 @@ //frankly, xaudio2 gives nothing over directsound, unless we're getting it to do all the mixing instead. which gets really messy and far too involved. //I suppose it has a use with WINRT... although that doesn't apply to any actual users. + //we're lazy and don't do any special threading, this makes it inferior to the directsound implementation - potentially, the callback feature could allow for slightly lower latencies. +//also no reverb (fixme: XAUDIO2FX_REVERB_PARAMETERS). //dxsdk = 2.7 = win7+ //w8sdk = 2.8 = win8+ diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index 0ecd2381..e9db106d 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -237,7 +237,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define ZYMOTICMODELS //zymotic skeletal models. #define DPMMODELS //darkplaces model format (which I've never seen anyone use) #define PSKMODELS //PSK model format (ActorX stuff from UT, though not the format the game itself uses) -// #define HALFLIFEMODELS //halflife model support (experimental) + #define HALFLIFEMODELS //halflife model support (experimental) #define INTERQUAKEMODELS #define RAGDOLL diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index df2538eb..2ee5b2c8 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -885,6 +885,7 @@ struct vec3_t *anorm; vec3_t *anorms; vec3_t *anormt; + float lerp; vbo_t vbo; vbo_t *vbop; @@ -1671,6 +1672,8 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in { if (meshcache.vertgroup == inf->shares_verts && meshcache.ent == e && usebones == meshcache.usebones) { + mesh->xyz_blendw[0] = meshcache.lerp; + mesh->xyz_blendw[1] = 1-meshcache.lerp; mesh->xyz_array = meshcache.acoords1; mesh->xyz2_array = meshcache.acoords2; mesh->normals_array = meshcache.anorm; @@ -1976,6 +1979,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in meshcache.anorm = mesh->normals_array; meshcache.anorms = mesh->snormals_array; meshcache.anormt = mesh->tnormals_array; + meshcache.lerp = mesh->xyz_blendw[0]; if (vbop) meshcache.vbop = *vbop; @@ -3987,36 +3991,35 @@ qboolean QDECL Mod_LoadQ2Model (model_t *mod, void *buffer, size_t fsize) int Mod_GetNumBones(model_t *model, qboolean allowtags) { - galiasinfo_t *inf; - - - if (!model || model->type != mod_alias) - return 0; - - inf = Mod_Extradata(model); + if (model && model->type == mod_alias) + { + galiasinfo_t *inf = Mod_Extradata(model); #ifdef SKELETALMODELS - if (inf->numbones) - return inf->numbones; - else + if (inf->numbones) + return inf->numbones; + else #endif - if (allowtags) - return inf->numtags; - else + if (allowtags) + return inf->numtags; return 0; + } +#ifdef HALFLIFEMODELS + if (model && model->type == mod_halflife) + return HLMDL_GetNumBones(model); +#endif + return 0; } int Mod_GetBoneRelations(model_t *model, int firstbone, int lastbone, framestate_t *fstate, float *result) { #ifdef SKELETALMODELS - galiasinfo_t *inf; - - - if (!model || model->type != mod_alias) - return false; - - inf = Mod_Extradata(model); - return Alias_BlendBoneData(inf, fstate, result, SKEL_RELATIVE, firstbone, lastbone); + if (model && model->type == mod_alias) + return Alias_BlendBoneData(Mod_Extradata(model), fstate, result, SKEL_RELATIVE, firstbone, lastbone); +#endif +#ifdef HALFLIFEMODELS + if (model && model->type == mod_halflife) + return HLMDL_GetBoneData(model, firstbone, lastbone, fstate, result); #endif return 0; } @@ -4272,7 +4275,7 @@ int Mod_TagNumForName(model_t *model, const char *name) return 0; #ifdef HALFLIFEMODELS if (model->type == mod_halflife) - return HLMod_BoneForName(model, name); + return HLMDL_BoneForName(model, name); #endif if (model->type != mod_alias) return 0; @@ -4310,7 +4313,7 @@ int Mod_FrameNumForName(model_t *model, int surfaceidx, const char *name) return -1; #ifdef HALFLIFEMODELS if (model->type == mod_halflife) - return HLMod_FrameForName(model, name); + return HLMDL_FrameForName(model, name); #endif if (model->type != mod_alias) return 0; @@ -4364,18 +4367,23 @@ const char *Mod_FrameNameForNum(model_t *model, int surfaceidx, int num) if (!model) return NULL; - if (model->type != mod_alias) - return NULL; + if (model->type == mod_alias) + { + inf = Mod_Extradata(model); - inf = Mod_Extradata(model); + while(surfaceidx-->0 && inf) + inf = inf->nextsurf; - while(surfaceidx-->0 && inf) - inf = inf->nextsurf; - - if (!inf || num >= inf->numanimations) - return NULL; - group = inf->ofsanimations; - return group[num].name; + if (!inf || num >= inf->numanimations) + return NULL; + group = inf->ofsanimations; + return group[num].name; + } +#ifdef HALFLIFEMODELS + if (model->type == mod_halflife) + return HLMDL_FrameNameForNum(model, surfaceidx, num); +#endif + return NULL; } qboolean Mod_FrameInfoForNum(model_t *model, int surfaceidx, int num, char **name, int *numframes, float *duration, qboolean *loop) @@ -4385,23 +4393,28 @@ qboolean Mod_FrameInfoForNum(model_t *model, int surfaceidx, int num, char **nam if (!model) return false; - if (model->type != mod_alias) - return false; + if (model->type == mod_alias) + { + inf = Mod_Extradata(model); - inf = Mod_Extradata(model); + while(surfaceidx-->0 && inf) + inf = inf->nextsurf; - while(surfaceidx-->0 && inf) - inf = inf->nextsurf; + if (!inf || num >= inf->numanimations) + return false; + group = inf->ofsanimations; - if (!inf || num >= inf->numanimations) - return false; - group = inf->ofsanimations; - - *name = group[num].name; - *numframes = group[num].numposes; - *loop = group[num].loop; - *duration = group->numposes/group->rate; - return true; + *name = group[num].name; + *numframes = group[num].numposes; + *loop = group[num].loop; + *duration = group->numposes/group->rate; + return true; + } +#ifdef HALFLIFEMODELS + if (model->type == mod_halflife) + return HLMDL_FrameInfoForNum(model, surfaceidx, num, name, numframes, duration, loop); +#endif + return false; } #ifndef SERVERONLY diff --git a/engine/common/com_mesh.h b/engine/common/com_mesh.h index ef891e5c..c554f8fb 100644 --- a/engine/common/com_mesh.h +++ b/engine/common/com_mesh.h @@ -9,8 +9,9 @@ extern "C" { #include #endif -int HLMod_BoneForName(model_t *mod, const char *name); -int HLMod_FrameForName(model_t *mod, const char *name); +#ifdef HALFLIFEMODELS +#include "model_hl.h" +#endif //a single pose within an animation (note: always refered to via a framegroup, even if there's only one frame in that group). typedef struct @@ -223,14 +224,10 @@ qboolean Mod_FrameInfoForNum(model_t *model, int surfaceidx, int num, char **nam void Mod_DoCRC(model_t *mod, char *buffer, int buffersize); -qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer, size_t fsize); -#ifdef MAP_PROC - qboolean Mod_LoadMap_Proc(model_t *mode, void *buffer); -#endif - void Mod_AccumulateTextureVectors(vecV_t *vc, vec2_t *tc, vec3_t *nv, vec3_t *sv, vec3_t *tv, index_t *idx, int numidx); void Mod_AccumulateMeshTextureVectors(mesh_t *mesh); void Mod_NormaliseTextureVectors(vec3_t *n, vec3_t *s, vec3_t *t, int v); +void R_Generate_Mesh_ST_Vectors(mesh_t *mesh); #ifdef __cplusplus }; diff --git a/engine/common/log.c b/engine/common/log.c index 3a85126c..b5440e72 100644 --- a/engine/common/log.c +++ b/engine/common/log.c @@ -17,7 +17,7 @@ cvar_t log_name[LOG_TYPES] = { CVARFC("log_name", "", CVAR_NOTFROMSERVER, Log_N CVARFC("log_name_rcon", "rcon", CVAR_NOTFROMSERVER, Log_Name_Callback)}; cvar_t log_dir = CVARFC("log_dir", "", CVAR_NOTFROMSERVER, Log_Dir_Callback); cvar_t log_readable = CVARFD("log_readable", "7", CVAR_NOTFROMSERVER, "Bitfield describing what to convert/strip. If 0, exact byte representation will be used.\n&1: Dequakify text.\n&2: Strip special markup.\n&4: Strip ansi control codes."); -cvar_t log_developer = CVARF("log_developer", "0", CVAR_NOTFROMSERVER); +cvar_t log_developer = CVARFD("log_developer", "0", CVAR_NOTFROMSERVER, "Enables logging of console prints when set to 1. Otherwise unimportant messages will not fill up your log files."); cvar_t log_rotate_files = CVARF("log_rotate_files", "0", CVAR_NOTFROMSERVER); cvar_t log_rotate_size = CVARF("log_rotate_size", "131072", CVAR_NOTFROMSERVER); cvar_t log_timestamps = CVARF("log_timestamps", "1", CVAR_NOTFROMSERVER); diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 7d884dfc..36a30e26 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -1708,6 +1708,29 @@ void QCBUILTIN PF_hash_destroytab (pubprogfuncs_t *prinst, struct globalvars_s * Z_Free(tab->bucketmem); } } +void PF_Hash_DestroyAll(pubprogfuncs_t *prinst) +{ + qboolean freed = true; + size_t idx; + for (idx = 0; idx < pf_hash_maxtables; idx++) + { + if (pf_hashtab[idx].prinst == prinst) + { + pf_hashtab[idx].prinst = NULL; + Hash_Enumerate(&pf_hashtab[idx].tab, PF_hash_destroytab_enum, NULL); + Z_Free(pf_hashtab[idx].bucketmem); + } + else if (pf_hashtab[idx].prinst) + freed = false; + } + + if (freed && pf_hashtab) + { + pf_hash_maxtables = 0; + Z_Free(pf_hashtab); + pf_hashtab = NULL; + } +} void QCBUILTIN PF_hash_createtab (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { //FIXME: these need to be managed by the qcvm for garbage collection @@ -2567,6 +2590,7 @@ void PR_fclose_progs (pubprogfuncs_t *prinst) { PF_fcloseall(prinst); search_close_progs(prinst, true); + PF_Hash_DestroyAll(prinst); } //File access diff --git a/engine/d3d/d3d11_backend.c b/engine/d3d/d3d11_backend.c index a1d2327a..0c8994c7 100644 --- a/engine/d3d/d3d11_backend.c +++ b/engine/d3d/d3d11_backend.c @@ -159,6 +159,8 @@ typedef struct vec3_t e_light_dir; float pad2; vec3_t e_light_mul; float pad3; vec4_t e_lmscale[4]; + vec4_t e_uppercolour; + vec4_t e_lowercolour; } cbuf_entity_t; //vertex attributes @@ -2781,6 +2783,34 @@ void D3D11BE_SetupLightCBuffer(dlight_t *l, vec3_t colour) ID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)shaderstate.lcbuffer, 0); } +static void R_FetchPlayerColour(unsigned int cv, vec4_t rgba) +{ + int i; + + if (cv >= 16) + { + rgba[0] = (((cv&0xff0000)>>16)**((unsigned char*)&d_8to24rgbtable[15]+0)) / (256.0*256); + rgba[1] = (((cv&0x00ff00)>>8)**((unsigned char*)&d_8to24rgbtable[15]+1)) / (256.0*256); + rgba[2] = (((cv&0x0000ff)>>0)**((unsigned char*)&d_8to24rgbtable[15]+2)) / (256.0*256); + rgba[3] = 1.0; + return; + } + i = cv; + if (i >= 8) + { + i<<=4; + } + else + { + i<<=4; + i+=15; + } + i*=3; + rgba[0] = host_basepal[i+0] / 255.0; + rgba[1] = host_basepal[i+1] / 255.0; + rgba[2] = host_basepal[i+2] / 255.0; + rgba[3] = 1.0; +} //also updates the entity constant buffer static void BE_RotateForEntity (const entity_t *e, const model_t *mod) @@ -2968,6 +2998,9 @@ static void BE_RotateForEntity (const entity_t *e, const model_t *mod) VectorCopy(e->light_dir, cbe->e_light_dir); VectorCopy(e->light_range, cbe->e_light_mul); + R_FetchPlayerColour(e->topcolour, cbe->e_uppercolour); + R_FetchPlayerColour(e->bottomcolour, cbe->e_lowercolour); + //various stuff in modelspace Matrix4x4_CM_Transform3(modelinv, r_origin, cbe->e_eyepos); diff --git a/engine/d3d/d3d11_shader.c b/engine/d3d/d3d11_shader.c index b8c2123e..19882c6c 100644 --- a/engine/d3d/d3d11_shader.c +++ b/engine/d3d/d3d11_shader.c @@ -153,6 +153,8 @@ HRESULT STDMETHODCALLTYPE d3dinclude_Open(ID3DInclude *this, D3D_INCLUDE_TYPE In "float3 e_light_dir; float pad2;\n" "float3 e_light_mul; float pad3;\n" "float4 e_lmscale[4];\n" + "float4 e_uppercolour;\n" + "float4 e_lowercolour;\n" "};\n" "cbuffer fteviewdefs : register(b1)\n" "{\n" @@ -473,12 +475,31 @@ qboolean D3D11Shader_CreateProgram (program_t *prog, const char *name, unsigned for (; *precompilerconstants; precompilerconstants++) { - const char *t = *precompilerconstants; - t = COM_Parse(t); - t = COM_Parse(t); - defines[consts].Name = Z_StrDup(com_token); - defines[consts].Definition = t?Z_StrDup(t):NULL; - consts++; + const char *t, *nl; + char *v; + for (t = *precompilerconstants; *t; t++) + { + t = COM_Parse(t); + t = COM_Parse(t); + defines[consts].Name = Z_StrDup(com_token); + while (*t == ' ' || *t == '\t') + t++; + nl = strchr(t, '\n'); + if (!nl) + nl = nl+strlen(nl); + + if (nl && nl != t) + { + v = BZ_Malloc(nl-t+1); + memcpy(v, t, nl-t); + v[nl-t] = 0; + defines[consts].Definition = v; + } + else + defines[consts].Definition = t?Z_StrDup(t):NULL; + consts++; + t = nl; + } } defines[consts].Name = NULL; diff --git a/engine/d3d/d3d_shader.c b/engine/d3d/d3d_shader.c index 5bf21767..d0ea3ad3 100644 --- a/engine/d3d/d3d_shader.c +++ b/engine/d3d/d3d_shader.c @@ -178,10 +178,29 @@ static qboolean D3D9Shader_CreateProgram (program_t *prog, const char *sname, un for (defbufe = defbuf; *precompilerconstants; precompilerconstants++) { - defines[consts].Definition = COM_ParseOut(*precompilerconstants+7, defbufe, defbuf+sizeof(defbuf) - defbufe-1); - defines[consts].Name = defbufe; - defbufe += strlen(defbufe)+1; - consts++; + const char *l, *nl; + for(l = *precompilerconstants; *l; l = nl) + { + l += 7; //skip over the assumed #define + + l = COM_ParseOut(l, defbufe, defbuf+sizeof(defbuf) - defbufe-1); + defines[consts].Name = defbufe; + defbufe += strlen(defbufe)+1; + + while (*l == ' ' || *l == '\t') + l++; + + nl = strchr(l, '\n'); + if (nl && *nl) + { + defines[consts++].Definition = defbufe; + memcpy(defbufe, l, nl-l); + defbufe[nl-l] = 0; + defbufe += nl++-l+1; + } + else + defines[consts++].Definition = l; + } } defines[consts].Name = NULL; diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index d72f5ea1..971a7fc1 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -29,6 +29,7 @@ extern cvar_t gl_overbright; extern cvar_t gl_ati_truform; extern cvar_t r_wireframe; extern cvar_t r_refract_fbo; +extern cvar_t r_refractreflect_scale; extern texid_t missing_texture; extern texid_t missing_texture_gloss; @@ -103,12 +104,12 @@ struct { texid_t tex_sourcedepth; texid_t tex_reflectcube;/*used where $reflectcube was invalid or failed*/ fbostate_t fbo_2dfbo; - fbostate_t fbo_reflectrefrac; + fbostate_t fbo_reflectrefrac[R_MAX_RECURSE]; fbostate_t fbo_lprepass; - texid_t tex_reflection; /*basically a portal rendered to texture*/ - texid_t tex_refraction; /*the (culled) underwater view*/ - texid_t tex_refractiondepth; /*the (culled) underwater view*/ - texid_t tex_ripplemap; /*temp image for waves and things.*/ + texid_t tex_reflection[R_MAX_RECURSE]; /*basically a portal rendered to texture*/ + texid_t tex_refraction[R_MAX_RECURSE]; /*the (culled) underwater view*/ + texid_t tex_refractiondepth[R_MAX_RECURSE]; /*the (culled) underwater view*/ + texid_t tex_ripplemap[R_MAX_RECURSE]; /*temp image for waves and things.*/ int curpatchverts; qboolean force2d; @@ -1239,7 +1240,7 @@ static void Shader_BindTextureForPass(int tmu, const shaderpass_t *pass) t = shaderstate.tex_sourcedepth; break; case T_GEN_REFLECTION: - t = shaderstate.tex_reflection; + t = shaderstate.tex_reflection[r_refdef.recurse]; break; case T_GEN_REFRACTION: if (!r_refract_fboival) @@ -1247,13 +1248,13 @@ static void Shader_BindTextureForPass(int tmu, const shaderpass_t *pass) T_Gen_CurrentRender(tmu); return; } - t = shaderstate.tex_refraction; + t = shaderstate.tex_refraction[r_refdef.recurse]; break; case T_GEN_REFRACTIONDEPTH: - t = shaderstate.tex_refractiondepth; + t = shaderstate.tex_refractiondepth[r_refdef.recurse]; break; case T_GEN_RIPPLEMAP: - t = shaderstate.tex_ripplemap; + t = shaderstate.tex_ripplemap[r_refdef.recurse]; break; } GL_LazyBind(tmu, GL_TEXTURE_2D, t); @@ -1383,24 +1384,28 @@ void GenerateFogTexture(texid_t *tex, float density, float zscale) void GLBE_DestroyFBOs(void) { + size_t i; GLBE_FBO_Destroy(&shaderstate.fbo_2dfbo); - GLBE_FBO_Destroy(&shaderstate.fbo_reflectrefrac); GLBE_FBO_Destroy(&shaderstate.fbo_lprepass); - if (shaderstate.tex_reflection) + for (i = 0; i < R_MAX_RECURSE; i++) { - Image_DestroyTexture(shaderstate.tex_reflection); - shaderstate.tex_reflection = r_nulltex; - } - if (shaderstate.tex_refraction) - { - Image_DestroyTexture(shaderstate.tex_refraction); - shaderstate.tex_refraction = r_nulltex; - } - if (shaderstate.tex_refractiondepth) - { - Image_DestroyTexture(shaderstate.tex_refractiondepth); - shaderstate.tex_refractiondepth = r_nulltex; + GLBE_FBO_Destroy(&shaderstate.fbo_reflectrefrac[i]); + if (shaderstate.tex_reflection[i]) + { + Image_DestroyTexture(shaderstate.tex_reflection[i]); + shaderstate.tex_reflection[i] = r_nulltex; + } + if (shaderstate.tex_refraction[i]) + { + Image_DestroyTexture(shaderstate.tex_refraction[i]); + shaderstate.tex_refraction[i] = r_nulltex; + } + if (shaderstate.tex_refractiondepth[i]) + { + Image_DestroyTexture(shaderstate.tex_refractiondepth[i]); + shaderstate.tex_refractiondepth[i] = r_nulltex; + } } if (shaderstate.temptexture) { @@ -4643,6 +4648,10 @@ static void GLBE_SubmitMeshesPortals(batch_t **worldlist, batch_t *dynamiclist) { batch_t *batch, *masklists[2]; int i; + + if (!dynamiclist && !worldlist[SHADER_SORT_PORTAL]) + return; //no portals to draw + /*attempt to draw portal shaders*/ if (shaderstate.mode == BEM_STANDARD) { @@ -4668,19 +4677,19 @@ static void GLBE_SubmitMeshesPortals(batch_t **worldlist, batch_t *dynamiclist) //make sure the current scene doesn't draw over the portal where its not meant to. clamp depth so the near clip plane doesn't cause problems. if (gl_config.arb_depth_clamp) qglEnable(GL_DEPTH_CLAMP_ARB); + /*draw depth only, to mask it off*/ + GLBE_SelectMode(BEM_DEPTHONLY); for (i = 0; i < 2; i++) { for (batch = i?dynamiclist:worldlist[SHADER_SORT_PORTAL]; batch; batch = batch->next) { - if (batch->meshes == batch->firstmesh) - continue; +// if (batch->meshes == batch->firstmesh) +// continue; - /*draw depth only, to mask it off*/ - GLBE_SelectMode(BEM_DEPTHONLY); GLBE_SubmitBatch(batch); - GLBE_SelectMode(BEM_STANDARD); } } + GLBE_SelectMode(BEM_STANDARD); if (gl_config.arb_depth_clamp) qglDisable(GL_DEPTH_CLAMP_ARB); } @@ -4749,9 +4758,9 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) int oldfbo; float oldil; int oldbem; - //these flags require rendering some view as an fbo - if (r_refdef.recurse) + if (r_refdef.recurse == r_portalrecursion.ival || r_refdef.recurse == R_MAX_RECURSE) continue; + //these flags require rendering some view as an fbo if (shaderstate.mode != BEM_STANDARD && shaderstate.mode != BEM_DEPTHDARK) continue; oldbem = shaderstate.mode; @@ -4759,36 +4768,37 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) if ((bs->flags & SHADER_HASREFLECT) && gl_config.ext_framebuffer_objects) { + float renderscale = r_refractreflect_scale.value; vrect_t orect = r_refdef.vrect; pxrect_t oprect = r_refdef.pxrect; - if (!shaderstate.tex_reflection) + if (!shaderstate.tex_reflection[r_refdef.recurse]) { - shaderstate.tex_reflection = Image_CreateTexture("***tex_reflection***", NULL, 0); - if (!shaderstate.tex_reflection->num) - qglGenTextures(1, &shaderstate.tex_reflection->num); + shaderstate.tex_reflection[r_refdef.recurse] = Image_CreateTexture("***tex_reflection***", NULL, 0); + if (!shaderstate.tex_reflection[r_refdef.recurse]->num) + qglGenTextures(1, &shaderstate.tex_reflection[r_refdef.recurse]->num); } r_refdef.vrect.x = 0; r_refdef.vrect.y = 0; - r_refdef.vrect.width = vid.fbvwidth/2; - r_refdef.vrect.height = vid.fbvheight/2; + r_refdef.vrect.width = max(1, vid.fbvwidth * renderscale); + r_refdef.vrect.height = max(1, vid.fbvheight * renderscale); r_refdef.pxrect.x = 0; r_refdef.pxrect.y = 0; - r_refdef.pxrect.width = vid.fbpwidth/2; - r_refdef.pxrect.height = vid.fbpheight/2; - if (shaderstate.tex_reflection->width!=r_refdef.pxrect.width || shaderstate.tex_reflection->height!=r_refdef.pxrect.height) + r_refdef.pxrect.width = max(1, vid.fbpwidth * renderscale); + r_refdef.pxrect.height = max(1, vid.fbpheight * renderscale); + if (shaderstate.tex_reflection[r_refdef.recurse]->width!=r_refdef.pxrect.width || shaderstate.tex_reflection[r_refdef.recurse]->height!=r_refdef.pxrect.height) { - shaderstate.tex_reflection->width = r_refdef.pxrect.width; - shaderstate.tex_reflection->height = r_refdef.pxrect.height; - GL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_reflection); - qglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, shaderstate.tex_reflection->width, shaderstate.tex_reflection->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + shaderstate.tex_reflection[r_refdef.recurse]->width = r_refdef.pxrect.width; + shaderstate.tex_reflection[r_refdef.recurse]->height = r_refdef.pxrect.height; + GL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_reflection[r_refdef.recurse]); + qglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, shaderstate.tex_reflection[r_refdef.recurse]->width, shaderstate.tex_reflection[r_refdef.recurse]->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } - oldfbo = GLBE_FBO_Update(&shaderstate.fbo_reflectrefrac, FBO_RB_DEPTH, &shaderstate.tex_reflection, 1, r_nulltex, shaderstate.tex_reflection->width, shaderstate.tex_reflection->height, 0); - r_refdef.pxrect.maxheight = shaderstate.fbo_reflectrefrac.rb_size[1]; + oldfbo = GLBE_FBO_Update(&shaderstate.fbo_reflectrefrac[r_refdef.recurse], FBO_RB_DEPTH, &shaderstate.tex_reflection[r_refdef.recurse], 1, r_nulltex, shaderstate.tex_reflection[r_refdef.recurse]->width, shaderstate.tex_reflection[r_refdef.recurse]->height, 0); + r_refdef.pxrect.maxheight = shaderstate.fbo_reflectrefrac[r_refdef.recurse].rb_size[1]; GL_ViewportUpdate(); GL_ForceDepthWritable(); qglClearColor(0, 0, 0, 1); @@ -4803,28 +4813,29 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) { if (r_refract_fboival) { + float renderscale = min(1, r_refractreflect_scale.value); vrect_t ovrect = r_refdef.vrect; pxrect_t oprect = r_refdef.pxrect; r_refdef.vrect.x = 0; r_refdef.vrect.y = 0; - r_refdef.vrect.width = vid.fbvwidth/2; - r_refdef.vrect.height = vid.fbvheight/2; + r_refdef.vrect.width = max(1, vid.fbvwidth * renderscale); + r_refdef.vrect.height = max(1, vid.fbvheight * renderscale); r_refdef.pxrect.x = 0; r_refdef.pxrect.y = 0; - r_refdef.pxrect.width = vid.fbpwidth/2; - r_refdef.pxrect.height = vid.fbpheight/2; + r_refdef.pxrect.width = max(1, vid.fbpwidth * renderscale); + r_refdef.pxrect.height = max(1, vid.fbpheight * renderscale); - if (!shaderstate.tex_refraction) + if (!shaderstate.tex_refraction[r_refdef.recurse]) { - shaderstate.tex_refraction = Image_CreateTexture("***tex_refraction***", NULL, 0); - if (!shaderstate.tex_refraction->num) - qglGenTextures(1, &shaderstate.tex_refraction->num); + shaderstate.tex_refraction[r_refdef.recurse] = Image_CreateTexture("***tex_refraction***", NULL, 0); + if (!shaderstate.tex_refraction[r_refdef.recurse]->num) + qglGenTextures(1, &shaderstate.tex_refraction[r_refdef.recurse]->num); } - if (shaderstate.tex_refraction->width != r_refdef.pxrect.width || shaderstate.tex_refraction->height != r_refdef.pxrect.height) + if (shaderstate.tex_refraction[r_refdef.recurse]->width != r_refdef.pxrect.width || shaderstate.tex_refraction[r_refdef.recurse]->height != r_refdef.pxrect.height) { - shaderstate.tex_refraction->width = r_refdef.pxrect.width; - shaderstate.tex_refraction->height = r_refdef.pxrect.height; - GL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_refraction); + shaderstate.tex_refraction[r_refdef.recurse]->width = r_refdef.pxrect.width; + shaderstate.tex_refraction[r_refdef.recurse]->height = r_refdef.pxrect.height; + GL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_refraction[r_refdef.recurse]); qglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, r_refdef.pxrect.width, r_refdef.pxrect.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -4833,30 +4844,30 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) } if (bs->flags & SHADER_HASREFRACTDEPTH) { - if (!shaderstate.tex_refractiondepth) + if (!shaderstate.tex_refractiondepth[r_refdef.recurse]) { - shaderstate.tex_refractiondepth = Image_CreateTexture("***tex_refractiondepth***", NULL, 0); - if (!shaderstate.tex_refractiondepth->num) - qglGenTextures(1, &shaderstate.tex_refractiondepth->num); + shaderstate.tex_refractiondepth[r_refdef.recurse] = Image_CreateTexture("***tex_refractiondepth***", NULL, 0); + if (!shaderstate.tex_refractiondepth[r_refdef.recurse]->num) + qglGenTextures(1, &shaderstate.tex_refractiondepth[r_refdef.recurse]->num); } - if (shaderstate.tex_refractiondepth->width != r_refdef.pxrect.width || shaderstate.tex_refractiondepth->height != r_refdef.pxrect.height) + if (shaderstate.tex_refractiondepth[r_refdef.recurse]->width != r_refdef.pxrect.width || shaderstate.tex_refractiondepth[r_refdef.recurse]->height != r_refdef.pxrect.height) { - shaderstate.tex_refractiondepth->width = r_refdef.pxrect.width; - shaderstate.tex_refractiondepth->height = r_refdef.pxrect.height; - GL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_refractiondepth); + shaderstate.tex_refractiondepth[r_refdef.recurse]->width = r_refdef.pxrect.width; + shaderstate.tex_refractiondepth[r_refdef.recurse]->height = r_refdef.pxrect.height; + GL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_refractiondepth[r_refdef.recurse]); qglTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24_ARB, r_refdef.pxrect.width, r_refdef.pxrect.height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } - oldfbo = GLBE_FBO_Update(&shaderstate.fbo_reflectrefrac, FBO_TEX_DEPTH, &shaderstate.tex_refraction, 1, shaderstate.tex_refractiondepth, r_refdef.pxrect.width, r_refdef.pxrect.height, 0); + oldfbo = GLBE_FBO_Update(&shaderstate.fbo_reflectrefrac[r_refdef.recurse], FBO_TEX_DEPTH, &shaderstate.tex_refraction[r_refdef.recurse], 1, shaderstate.tex_refractiondepth[r_refdef.recurse], r_refdef.pxrect.width, r_refdef.pxrect.height, 0); } else { - oldfbo = GLBE_FBO_Update(&shaderstate.fbo_reflectrefrac, FBO_RB_DEPTH, &shaderstate.tex_refraction, 1, r_nulltex, r_refdef.pxrect.width, r_refdef.pxrect.height, 0); + oldfbo = GLBE_FBO_Update(&shaderstate.fbo_reflectrefrac[r_refdef.recurse], FBO_RB_DEPTH, &shaderstate.tex_refraction[r_refdef.recurse], 1, r_nulltex, r_refdef.pxrect.width, r_refdef.pxrect.height, 0); } - r_refdef.pxrect.maxheight = shaderstate.fbo_reflectrefrac.rb_size[1]; + r_refdef.pxrect.maxheight = shaderstate.fbo_reflectrefrac[r_refdef.recurse].rb_size[1]; GL_ViewportUpdate(); GL_ForceDepthWritable(); @@ -4874,37 +4885,38 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) } if ((bs->flags & SHADER_HASRIPPLEMAP) && gl_config.ext_framebuffer_objects) { + float renderscale = r_refractreflect_scale.value; vrect_t orect = r_refdef.vrect; pxrect_t oprect = r_refdef.pxrect; r_refdef.vrect.x = 0; r_refdef.vrect.y = 0; - r_refdef.vrect.width = vid.fbvwidth/2; - r_refdef.vrect.height = vid.fbvheight/2; + r_refdef.vrect.width = max(1, vid.fbvwidth * renderscale); + r_refdef.vrect.height = max(1, vid.fbvheight * renderscale); r_refdef.pxrect.x = 0; r_refdef.pxrect.y = 0; - r_refdef.pxrect.width = vid.fbpwidth/2; - r_refdef.pxrect.height = vid.fbpheight/2; + r_refdef.pxrect.width = max(1, vid.fbpwidth * renderscale); + r_refdef.pxrect.height = max(1, vid.fbpheight * renderscale); - if (!shaderstate.tex_ripplemap) + if (!shaderstate.tex_ripplemap[r_refdef.recurse]) { //FIXME: can we use RGB8 instead? - shaderstate.tex_ripplemap = Image_CreateTexture("***tex_ripplemap***", NULL, 0); - if (!shaderstate.tex_ripplemap->num) - qglGenTextures(1, &shaderstate.tex_ripplemap->num); + shaderstate.tex_ripplemap[r_refdef.recurse] = Image_CreateTexture("***tex_ripplemap***", NULL, 0); + if (!shaderstate.tex_ripplemap[r_refdef.recurse]->num) + qglGenTextures(1, &shaderstate.tex_ripplemap[r_refdef.recurse]->num); } - if (shaderstate.tex_ripplemap->width != r_refdef.pxrect.width || shaderstate.tex_ripplemap->height != r_refdef.pxrect.height) + if (shaderstate.tex_ripplemap[r_refdef.recurse]->width != r_refdef.pxrect.width || shaderstate.tex_ripplemap[r_refdef.recurse]->height != r_refdef.pxrect.height) { - shaderstate.tex_ripplemap->width = r_refdef.pxrect.width; - shaderstate.tex_ripplemap->height = r_refdef.pxrect.height; - GL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_ripplemap); + shaderstate.tex_ripplemap[r_refdef.recurse]->width = r_refdef.pxrect.width; + shaderstate.tex_ripplemap[r_refdef.recurse]->height = r_refdef.pxrect.height; + GL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_ripplemap[r_refdef.recurse]); qglTexImage2D(GL_TEXTURE_2D, 0, /*(gl_config.glversion>3.1)?GL_RGBA8_SNORM:*/GL_RGBA16F_ARB, r_refdef.pxrect.width, r_refdef.pxrect.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } - oldfbo = GLBE_FBO_Update(&shaderstate.fbo_reflectrefrac, 0, &shaderstate.tex_ripplemap, 1, r_nulltex, r_refdef.pxrect.width, r_refdef.pxrect.height, 0); - r_refdef.pxrect.maxheight = shaderstate.fbo_reflectrefrac.rb_size[1]; + oldfbo = GLBE_FBO_Update(&shaderstate.fbo_reflectrefrac[r_refdef.recurse], 0, &shaderstate.tex_ripplemap[r_refdef.recurse], 1, r_nulltex, r_refdef.pxrect.width, r_refdef.pxrect.height, 0); + r_refdef.pxrect.maxheight = shaderstate.fbo_reflectrefrac[r_refdef.recurse].rb_size[1]; GL_ViewportUpdate(); qglClearColor(0, 0, 0, 1); @@ -5058,7 +5070,7 @@ void GLBE_RenderToTextureUpdate2d(qboolean destchanged) else GLBE_FBO_Push(NULL); - GL_Set2D(false); + GL_Set2D(false); } else { diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index 2cb49063..3323f8df 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -18,6 +18,18 @@ cvar_t mod_terrain_networked = CVARD("mod_terrain_networked", "0", "Terrain edit cvar_t mod_terrain_defaulttexture = CVARD("mod_terrain_defaulttexture", "", "Newly created terrain tiles will use this texture. This should generally be updated by the terrain editor."); cvar_t mod_terrain_savever = CVARD("mod_terrain_savever", "", "Which terrain section version to write if terrain was edited."); +enum +{ + hmcmd_brush_delete, + hmcmd_brush_insert, + hmcmd_prespawning, //sent before initial inserts + hmcmd_prespawned, //sent just after initial inserts + + hmcmd_ent_edit = 0x40, + hmcmd_ent_remove +}; + + void validatelinks(link_t *firstnode) { /* link_t *node; @@ -4808,8 +4820,9 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g vec3_t pos;// G_VECTOR(OFS_PARM1); float radius = G_FLOAT(OFS_PARM2); float quant = G_FLOAT(OFS_PARM3); + int modelindex = ((wedict_t*)PROG_TO_EDICT(prinst, *vmw->g.self))->v->modelindex; // G_FLOAT(OFS_RETURN) = Heightmap_Edit(w->worldmodel, action, pos, radius, quant); - model_t *mod = vmw->Get_CModel(vmw, ((wedict_t*)PROG_TO_EDICT(prinst, *vmw->g.self))->v->modelindex); + model_t *mod = vmw->Get_CModel(vmw, modelindex); heightmap_t *hm; vec4_t tally; @@ -4828,16 +4841,86 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g case ter_ent_get: { int idx = G_INT(OFS_PARM1); - G_INT(OFS_RETURN) = 0; + if (!mod->numentityinfo) + Mod_ParseEntities(mod); + if (idx >= mod->numentityinfo || !mod->entityinfo[idx].keyvals) + G_INT(OFS_RETURN) = 0; + else + G_INT(OFS_RETURN) = PR_TempString(prinst, mod->entityinfo[idx].keyvals); } return; case ter_ent_set: { int idx = G_INT(OFS_PARM1); - const char *news = PR_GetStringOfs(prinst, OFS_PARM2); + const char *newvals; + if (!mod->numentityinfo) + Mod_ParseEntities(mod); + if (idx < mod->numentityinfo) + { + Z_Free(mod->entityinfo[idx].keyvals); + mod->entityinfo[idx].keyvals = NULL; + } + if (G_INT(OFS_PARM2)) + { + newvals = PR_GetStringOfs(prinst, OFS_PARM2); + if (idx >= mod->numentityinfo) + Z_ReallocElements(&mod->entityinfo, &mod->numentityinfo, idx+64, sizeof(*mod->entityinfo)); + mod->entityinfo[idx].keyvals = Z_StrDup(newvals); + } + else + newvals = NULL; G_INT(OFS_RETURN) = 0; + +#ifndef CLIENTONLY + if (sv.state && modelindex > 0) + { + MSG_WriteByte(&sv.multicast, svcfte_brushedit); + MSG_WriteShort(&sv.multicast, modelindex); + MSG_WriteByte(&sv.multicast, newvals?hmcmd_ent_edit:hmcmd_ent_remove); + MSG_WriteLong(&sv.multicast, idx); + if (newvals) + MSG_WriteString(&sv.multicast, newvals); + SV_MulticastProtExt(vec3_origin, MULTICAST_ALL_R, ~0, 0, 0); + //tell ssqc, csqc will be told by the server + } + else +#endif +#ifndef SERVERONLY + if (cls.state && modelindex > 0) + { + MSG_WriteByte(&cls.netchan.message, clcfte_brushedit); + MSG_WriteShort(&cls.netchan.message, modelindex); + MSG_WriteByte(&cls.netchan.message, newvals?hmcmd_ent_edit:hmcmd_ent_remove); + MSG_WriteLong(&cls.netchan.message, idx); + if (newvals) + MSG_WriteString(&cls.netchan.message, newvals); + + #ifdef CSQC_DAT + CSQC_MapEntityEdited(idx, newvals); + #endif + } + else +#endif + { + #ifdef CSQC_DAT + CSQC_MapEntityEdited(idx, newvals); + #endif + } } return; + case ter_ent_add: + { + int idx = G_INT(OFS_PARM1); + const char *news = PR_GetStringOfs(prinst, OFS_PARM2); + G_INT(OFS_RETURN) = mod->numentityinfo; + + } + return; + case ter_ent_count: + if (!mod->numentityinfo) + Mod_ParseEntities(mod); + G_INT(OFS_RETURN) = mod->numentityinfo; + return; case ter_ents_wipe: G_INT(OFS_RETURN) = PR_TempString(prinst, Mod_GetEntitiesString(mod)); Mod_SetEntitiesString(mod, "", true); @@ -6018,6 +6101,7 @@ static qboolean Brush_Deserialise(heightmap_t *hm, brushes_t *br) } return true; } + #ifndef SERVERONLY heightmap_t *CL_BrushEdit_ForceContext(model_t *mod) { @@ -6050,14 +6134,20 @@ void CL_Parse_BrushEdit(void) model_t *mod = (modelindexterrain:NULL; - if (cmd == 0) +#ifdef CLIENTONLY + const qboolean ignore = false; +#else + const qboolean ignore = (sv.state>=ss_loading); //if we're the server then we already have this info. don't break anything (this info is present for demos). +#endif + + if (cmd == hmcmd_brush_delete) { int id = MSG_ReadLong(); - if (sv.state >= ss_loading) + if (ignore) return; //ignore if we're the server, we should already have it anyway. Terr_Brush_DeleteId(hm, id); } - else if (cmd == 1) + else if (cmd == hmcmd_brush_insert) //1=create/replace { brushes_t brush; @@ -6069,7 +6159,7 @@ void CL_Parse_BrushEdit(void) brush.faces = alloca(sizeof(*brush.faces) * brush.numplanes); if (!Brush_Deserialise(hm, &brush)) Host_EndGame("CL_Parse_BrushEdit: unparsable brush\n"); - if (sv.state >= ss_loading) + if (ignore) return; //ignore if we're the server, we should already have it anyway (but might need it for demos, hence why its still sent). if (brush.id) { @@ -6088,9 +6178,9 @@ void CL_Parse_BrushEdit(void) } Terr_Brush_Insert(mod, hm, &brush); } - else if (cmd == 2) - { - if (sv.state >= ss_loading) + else if (cmd == hmcmd_prespawning) + { //delete all + if (ignore) return; //ignore if we're the server, we should already have it anyway. hm = CL_BrushEdit_ForceContext(mod); //make sure we don't end up with any loaded brushes. @@ -6100,9 +6190,37 @@ void CL_Parse_BrushEdit(void) Terr_Brush_DeleteIdx(hm, hm->numbrushes-1); } } - else if (cmd == 3) + else if (cmd == hmcmd_prespawned) { - //follows edits after a 2 + } + else if (cmd == hmcmd_ent_edit) + { //ent edit + int id = MSG_ReadLong(); + const char *data = MSG_ReadString(); + if (id > 0xffff) + return; + if (id >= mod->numentityinfo) + Z_ReallocElements(&mod->entityinfo, &mod->numentityinfo, id+64, sizeof(*mod->entityinfo)); + if (id < mod->numentityinfo) + { + if (!ignore) + { + Z_Free(mod->entityinfo[id].keyvals); + mod->entityinfo[id].keyvals = Z_StrDup(data); + } + CSQC_MapEntityEdited(id, data); + } + } + else if (cmd == hmcmd_ent_remove) + { + int id = MSG_ReadLong(); + if (sv.state >= ss_loading) + return; //if we're the server then this will already have been done. don't clobber from internal latency + if (id < mod->numentityinfo) + { + Z_Free(mod->entityinfo[id].keyvals); + mod->entityinfo[id].keyvals = NULL; + } } else Host_EndGame("CL_Parse_BrushEdit: unknown command %i\n", cmd); @@ -6140,7 +6258,7 @@ qboolean SV_Prespawn_Brushes(sizebuf_t *msg, unsigned int *modelindex, unsigned { //make sure the client starts with a clean slate. MSG_WriteByte(msg, svcfte_brushedit); MSG_WriteShort(msg, *modelindex); - MSG_WriteByte(msg, 2); + MSG_WriteByte(msg, hmcmd_prespawning); } //weird loop to try to ensure we never miss any brushes. @@ -6161,7 +6279,7 @@ qboolean SV_Prespawn_Brushes(sizebuf_t *msg, unsigned int *modelindex, unsigned { MSG_WriteByte(msg, svcfte_brushedit); MSG_WriteShort(msg, *modelindex); - MSG_WriteByte(msg, 1); + MSG_WriteByte(msg, hmcmd_brush_insert); Brush_Serialise(msg, best); *lastid = bestid; return true; @@ -6178,8 +6296,8 @@ qboolean SV_Parse_BrushEdit(void) int cmd = MSG_ReadByte(); model_t *mod = (modelindexterrain:NULL; - if (cmd == 0) - { + if (cmd == hmcmd_brush_delete) + { //delete unsigned int brushid = MSG_ReadLong(); if (!authorise) { @@ -6190,12 +6308,12 @@ qboolean SV_Parse_BrushEdit(void) MSG_WriteByte(&sv.multicast, svcfte_brushedit); MSG_WriteShort(&sv.multicast, modelindex); - MSG_WriteByte(&sv.multicast, 0); + MSG_WriteByte(&sv.multicast, hmcmd_brush_delete); MSG_WriteLong(&sv.multicast, brushid); SV_MulticastProtExt(vec3_origin, MULTICAST_ALL_R, ~0, 0, 0); return true; } - else if (cmd == 1) + else if (cmd == hmcmd_brush_insert) { brushes_t brush; memset(&brush, 0, sizeof(brush)); @@ -6221,11 +6339,31 @@ qboolean SV_Parse_BrushEdit(void) MSG_WriteByte(&sv.multicast, svcfte_brushedit); MSG_WriteShort(&sv.multicast, modelindex); - MSG_WriteByte(&sv.multicast, 1); + MSG_WriteByte(&sv.multicast, hmcmd_brush_insert); Brush_Serialise(&sv.multicast, &brush); SV_MulticastProtExt(vec3_origin, MULTICAST_ALL_R, ~0, 0, 0); return true; } + else if (cmd == hmcmd_ent_edit || cmd == hmcmd_ent_remove) + { + size_t entid = MSG_ReadLong(); + char *keyvals = (cmd == hmcmd_ent_edit)?MSG_ReadString():NULL; + if (mod->submodelof != mod) + return true; + if (!authorise) + { + SV_PrintToClient(host_client, PRINT_MEDIUM, "Entity editing ignored: you are not a mapper\n"); + return true; + } + + MSG_WriteByte(&sv.multicast, svcfte_brushedit); + MSG_WriteShort(&sv.multicast, modelindex); + MSG_WriteByte(&sv.multicast, keyvals?hmcmd_ent_edit:hmcmd_ent_remove); + MSG_WriteLong(&sv.multicast, entid); + if (keyvals) + MSG_WriteString(&sv.multicast, keyvals); + SV_MulticastProtExt(vec3_origin, MULTICAST_ALL_R, ~0, 0, 0); + } else { Con_Printf("SV_Parse_BrushEdit: %s sent an unknown command: %i\n", host_client->name, cmd); @@ -6366,7 +6504,7 @@ void QCBUILTIN PF_brush_create(pubprogfuncs_t *prinst, struct globalvars_s *pr_g { MSG_WriteByte(&sv.multicast, svcfte_brushedit); MSG_WriteShort(&sv.multicast, modelindex); - MSG_WriteByte(&sv.multicast, 0); + MSG_WriteByte(&sv.multicast, hmcmd_brush_delete); MSG_WriteLong(&sv.multicast, brushid); SV_MulticastProtExt(vec3_origin, MULTICAST_ALL_R, ~0, 0, 0); } @@ -6377,7 +6515,7 @@ void QCBUILTIN PF_brush_create(pubprogfuncs_t *prinst, struct globalvars_s *pr_g { MSG_WriteByte(&cls.netchan.message, clcfte_brushedit); MSG_WriteShort(&cls.netchan.message, modelindex); - MSG_WriteByte(&cls.netchan.message, 0); + MSG_WriteByte(&cls.netchan.message, hmcmd_brush_delete); MSG_WriteLong(&cls.netchan.message, brushid); } #else @@ -6418,7 +6556,7 @@ void QCBUILTIN PF_brush_create(pubprogfuncs_t *prinst, struct globalvars_s *pr_g { MSG_WriteByte(&sv.multicast, svcfte_brushedit); MSG_WriteShort(&sv.multicast, modelindex); - MSG_WriteByte(&sv.multicast, 1); + MSG_WriteByte(&sv.multicast, hmcmd_brush_insert); Brush_Serialise(&sv.multicast, nb); SV_MulticastProtExt(vec3_origin, MULTICAST_ALL_R, ~0, 0, 0); return; @@ -6429,7 +6567,7 @@ void QCBUILTIN PF_brush_create(pubprogfuncs_t *prinst, struct globalvars_s *pr_g { MSG_WriteByte(&cls.netchan.message, clcfte_brushedit); MSG_WriteShort(&cls.netchan.message, modelindex); - MSG_WriteByte(&cls.netchan.message, 1); + MSG_WriteByte(&cls.netchan.message, hmcmd_brush_insert); Brush_Serialise(&cls.netchan.message, nb); return; } @@ -6456,7 +6594,7 @@ void QCBUILTIN PF_brush_delete(pubprogfuncs_t *prinst, struct globalvars_s *pr_g { MSG_WriteByte(&sv.multicast, svcfte_brushedit); MSG_WriteShort(&sv.multicast, modelindex); - MSG_WriteByte(&sv.multicast, 0); + MSG_WriteByte(&sv.multicast, hmcmd_brush_delete); MSG_WriteLong(&sv.multicast, brushid); SV_MulticastProtExt(vec3_origin, MULTICAST_ALL_R, ~0, 0, 0); return; @@ -6467,7 +6605,7 @@ void QCBUILTIN PF_brush_delete(pubprogfuncs_t *prinst, struct globalvars_s *pr_g { MSG_WriteByte(&cls.netchan.message, clcfte_brushedit); MSG_WriteShort(&cls.netchan.message, modelindex); - MSG_WriteByte(&cls.netchan.message, 0); + MSG_WriteByte(&cls.netchan.message, hmcmd_brush_delete); MSG_WriteLong(&cls.netchan.message, brushid); return; } diff --git a/engine/gl/gl_hlmdl.c b/engine/gl/gl_hlmdl.c index a5142711..35afe45a 100644 --- a/engine/gl/gl_hlmdl.c +++ b/engine/gl/gl_hlmdl.c @@ -3,6 +3,7 @@ #ifdef HALFLIFEMODELS #include "shader.h" +#include "com_mesh.h" /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Half-Life Model Renderer (Experimental) Copyright (C) 2001 James 'Ender' Brown [ender@quakesrc.org] This program is @@ -22,7 +23,6 @@ Also, please note that it won't do all hl models.... Nor will it work 100% */ -#include "model_hl.h" void QuaternionGLMatrix(float x, float y, float z, float w, vec4_t *GLM) { @@ -66,6 +66,140 @@ matrix3x4 transform_matrix[MAX_BONES]; /* Vertex transformation matrix */ void GL_Draw_HL_AliasFrame(short *order, vec3_t *transformed, float tex_w, float tex_h); +struct hlvremaps +{ + unsigned short vertidx; + unsigned short normalidx; + unsigned short scoord; + unsigned short tcoord; +}; +static index_t HLMDL_DeDupe(unsigned short *order, struct hlvremaps *rem, size_t *count) +{ + size_t i; + for (i = *count; i-- > 0;) + { + if (rem[i].vertidx == order[0] && rem[i].normalidx == order[1] && rem[i].scoord == order[2] && rem[i].tcoord == order[3]) + return i; + } + i = *count; + rem[i].vertidx = order[0]; + rem[i].normalidx = order[1]; + rem[i].scoord = order[2]; + rem[i].tcoord = order[3]; + *count += 1; + return i; +} + +//parse the vertex info, pull out what we can +static void HLMDL_PrepareVerticies (hlmodel_t *model, hlmdl_submodel_t *amodel, struct hlalternative_s *submodel) +{ + struct hlvremaps *uvert; + size_t uvertcount; + unsigned short count; + int i; + size_t idx = 0, v, m; + + mesh_t *mesh = &submodel->mesh; + index_t *index; + + vec3_t *verts = (vec3_t *) ((qbyte *) model->header + amodel->vertindex); + qbyte *bone = ((qbyte *) model->header + amodel->vertinfoindex); + vec3_t *norms = (vec3_t *) ((qbyte *) model->header + amodel->normindex); + + uvertcount = 0; + uvert = malloc(sizeof(*uvert)*2048); + + index = mesh->indexes = ZG_Malloc(model->memgroup, sizeof(*mesh->colors4b_array)*65536); + + for(m = 0; m < amodel->nummesh; m++) + { + hlmdl_mesh_t *inmesh = (hlmdl_mesh_t *) ((qbyte *) model->header + amodel->meshindex) + m; + unsigned short *order = (unsigned short *) ((qbyte *) model->header + inmesh->index); + + submodel->submesh[m].firstindex = mesh->numindexes; + submodel->submesh[m].numindexes = 0; + + for(;;) + { + count = *order++; /* get the vertex count and primitive type */ + if(!count) break; /* done */ + + if(count & 0x8000) + { //fan + int first = HLMDL_DeDupe(order+0*4, uvert, &uvertcount); + int prev = HLMDL_DeDupe(order+1*4, uvert, &uvertcount); + for (i = min(2,count); i < -(short)count; i++) + { + index[idx++] = first; + index[idx++] = prev; + index[idx++] = prev = HLMDL_DeDupe(order+i*4, uvert, &uvertcount); + } + } + else + { + int v0 = HLMDL_DeDupe(order+0*4, uvert, &uvertcount); + int v1 = HLMDL_DeDupe(order+1*4, uvert, &uvertcount); + //emit (count-2)*3 indicies as a strip + //012 213, etc + + for (i = min(2,count); i < count; i++) + { + if (i & 1) + { + index[idx++] = v1; + index[idx++] = v0; + } + else + { + index[idx++] = v0; + index[idx++] = v1; + } + v0 = v1; + index[idx++] = v1 = HLMDL_DeDupe(order+i*4, uvert, &uvertcount); + } + } + order += i*4; + } + + submodel->submesh[m].numindexes = idx - submodel->submesh[m].firstindex; + + } + mesh->numindexes = idx; + mesh->numvertexes = uvertcount; + + mesh->colors4b_array = ZG_Malloc(model->memgroup, sizeof(*mesh->colors4b_array)*uvertcount); + mesh->st_array = ZG_Malloc(model->memgroup, sizeof(*mesh->st_array)*uvertcount); + mesh->lmst_array[0] = ZG_Malloc(model->memgroup, sizeof(*mesh->lmst_array[0])*uvertcount); + mesh->xyz_array = ZG_Malloc(model->memgroup, sizeof(*mesh->xyz_array)*uvertcount); + mesh->normals_array = ZG_Malloc(model->memgroup, sizeof(*mesh->normals_array)*uvertcount); + mesh->snormals_array = ZG_Malloc(model->memgroup, sizeof(*mesh->snormals_array)*uvertcount); + mesh->tnormals_array = ZG_Malloc(model->memgroup, sizeof(*mesh->tnormals_array)*uvertcount); + mesh->bonenums = ZG_Malloc(model->memgroup, sizeof(*mesh->bonenums)*uvertcount); + mesh->boneweights = ZG_Malloc(model->memgroup, sizeof(*mesh->boneweights)*uvertcount); + + //prepare the verticies now that we have the mappings + for(v = 0; v < uvertcount; v++) + { + mesh->bonenums[v][0] = mesh->bonenums[v][1] = mesh->bonenums[v][2] = mesh->bonenums[v][3] = bone[uvert[v].vertidx]; + Vector4Set(mesh->boneweights[v], 1, 0, 0, 0); + Vector4Set(mesh->colors4b_array[v], 255, 255, 255, 255); //why bytes? why not? + + mesh->lmst_array[0][v][0] = uvert[v].scoord; + mesh->lmst_array[0][v][1] = uvert[v].tcoord; + VectorCopy(verts[uvert[v].vertidx], mesh->xyz_array[v]); + + //Warning: these models use different tables for vertex and normals. + //this means they might be transformed by different bones. we ignore that and just assume that the normals will want the same bone. + VectorCopy(norms[uvert[v].normalidx], mesh->normals_array[v]); + } + + //don't need that mapping any more + free(uvert); + + //treat this as the base pose, and calculate the sdir+tdir for bumpmaps. + R_Generate_Mesh_ST_Vectors(mesh); +} + /* ======================================================================================================================= Mod_LoadHLModel - read in the model's constituent parts @@ -73,10 +207,9 @@ void GL_Draw_HL_AliasFrame(short *order, vec3_t *transformed, float tex_w, float */ qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer, size_t fsize) { - /*~~*/ - int i; + int i; - hlmodelcache_t *model; + hlmodel_t *model; hlmdl_header_t *header; hlmdl_header_t *texheader; hlmdl_tex_t *tex; @@ -84,11 +217,12 @@ qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer, size_t fsize) hlmdl_bonecontroller_t *bonectls; struct hlmodelshaders_s *shaders; void *texmem = NULL; - /*~~*/ + int body; //load the model into hunk - model = ZG_Malloc(&mod->memgroup, sizeof(hlmodelcache_t)); + model = ZG_Malloc(&mod->memgroup, sizeof(hlmodel_t)); + model->memgroup = &mod->memgroup; header = ZG_Malloc(&mod->memgroup, fsize); memcpy(header, buffer, fsize); @@ -97,7 +231,7 @@ qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer, size_t fsize) //this is to let bigfoot know when he comes to port it all... And I'm lazy. #ifdef warningmsg #pragma warningmsg("-----------------------------------------") -#pragma warningmsg("FIXME: No byteswapping on halflife models") +#pragma warningmsg("FIXME: No byteswapping on halflife models") //hah, yeah, good luck with that, you'll need it. #pragma warningmsg("-----------------------------------------") #endif #endif @@ -141,24 +275,8 @@ qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer, size_t fsize) header->numtextures = texheader->numtextures; tex = (hlmdl_tex_t *) ((qbyte *) texheader + texheader->textures); - bones = (hlmdl_bone_t *) ((qbyte *) header + header->boneindex); - bonectls = (hlmdl_bonecontroller_t *) ((qbyte *) header + header->controllerindex); - - -/* won't work - doesn't know exact sizes. - - header = Hunk_Alloc(sizeof(hlmdl_header_t)); - memcpy(header, (hlmdl_header_t *) buffer, sizeof(hlmdl_header_t)); - - tex = Hunk_Alloc(sizeof(hlmdl_tex_t)*header->numtextures); - memcpy(tex, (hlmdl_tex_t *) buffer, sizeof(hlmdl_tex_t)*header->numtextures); - - bones = Hunk_Alloc(sizeof(hlmdl_bone_t)*header->numtextures); - memcpy(bones, (hlmdl_bone_t *) buffer, sizeof(hlmdl_bone_t)*header->numbones); - - bonectls = Hunk_Alloc(sizeof(hlmdl_bonecontroller_t)*header->numcontrollers); - memcpy(bonectls, (hlmdl_bonecontroller_t *) buffer, sizeof(hlmdl_bonecontroller_t)*header->numcontrollers); -*/ + bones = (hlmdl_bone_t *) ((qbyte *) header + header->boneindex); + bonectls = (hlmdl_bonecontroller_t *) ((qbyte *) header + header->controllerindex); model->header = header; model->bones = bones; @@ -166,18 +284,19 @@ qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer, size_t fsize) shaders = ZG_Malloc(&mod->memgroup, texheader->numtextures*sizeof(shader_t)); model->shaders = shaders; - for(i = 0; i < texheader->numtextures; i++) - { - Q_snprintfz(shaders[i].name, sizeof(shaders[i].name), "%s_%i.tga", mod->name, i); + for(i = 0; i < texheader->numtextures; i++) + { + Q_snprintfz(shaders[i].name, sizeof(shaders[i].name), "%s/%s", mod->name, COM_SkipPath(tex[i].name)); memset(&shaders[i].defaulttex, 0, sizeof(shaders[i].defaulttex)); shaders[i].defaulttex.base = Image_GetTexture(shaders[i].name, "", IF_NOALPHA, (qbyte *) texheader + tex[i].offset, (qbyte *) texheader + tex[i].w * tex[i].h + tex[i].offset, tex[i].w, tex[i].h, TF_8PAL24); shaders[i].w = tex[i].w; shaders[i].h = tex[i].h; - } + } - model->numskins = texheader->numtextures; - model->skins = ZG_Malloc(&mod->memgroup, model->numskins*sizeof(*model->skins)); - memcpy(model->skins, (short *) ((qbyte *) texheader + texheader->skins), model->numskins*sizeof(*model->skins)); + model->numskinrefs = texheader->skinrefs; + model->numskingroups = texheader->skingroups; + model->skinref = ZG_Malloc(&mod->memgroup, model->numskinrefs*model->numskingroups*sizeof(*model->skinref)); + memcpy(model->skinref, (short *) ((qbyte *) texheader + texheader->skins), model->numskinrefs*model->numskingroups*sizeof(*model->skinref)); if (texmem) @@ -185,6 +304,23 @@ qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer, size_t fsize) mod->type = mod_halflife; mod->meshinfo = model; + + model->numgeomsets = model->header->numbodyparts; + model->geomset = ZG_Malloc(&mod->memgroup, sizeof(*model->geomset) * model->numgeomsets); + for (body = 0; body < model->header->numbodyparts; body++) + { + hlmdl_bodypart_t *bodypart = (hlmdl_bodypart_t *) ((qbyte *) model->header + model->header->bodypartindex) + body; + int bodyindex; + model->geomset[body].numalternatives = bodypart->nummodels; + model->geomset[body].alternatives = ZG_Malloc(&mod->memgroup, sizeof(*model->geomset[body].alternatives) * bodypart->nummodels); + for (bodyindex = 0; bodyindex < bodypart->nummodels; bodyindex++) + { + hlmdl_submodel_t *amodel = (hlmdl_submodel_t *) ((qbyte *) model->header + bodypart->modelindex) + bodyindex; + model->geomset[body].alternatives[bodyindex].numsubmeshes = amodel->nummesh; + model->geomset[body].alternatives[bodyindex].submesh = ZG_Malloc(&mod->memgroup, sizeof(*model->geomset[body].alternatives[bodyindex].submesh) * amodel->nummesh); + HLMDL_PrepareVerticies(model, amodel, &model->geomset[body].alternatives[bodyindex]); + } + } return true; } @@ -200,12 +336,12 @@ void *Mod_GetHalfLifeModelData(model_t *mod) } #endif -int HLMod_FrameForName(model_t *mod, const char *name) +int HLMDL_FrameForName(model_t *mod, const char *name) { int i; hlmdl_header_t *h; hlmdl_sequencelist_t *seqs; - hlmodelcache_t *mc; + hlmodel_t *mc; if (!mod || mod->type != mod_halflife) return -1; //halflife models only, please @@ -222,12 +358,12 @@ int HLMod_FrameForName(model_t *mod, const char *name) return -1; } -int HLMod_BoneForName(model_t *mod, char *name) +int HLMDL_BoneForName(model_t *mod, const char *name) { int i; hlmdl_header_t *h; hlmdl_bone_t *bones; - hlmodelcache_t *mc; + hlmodel_t *mc; if (!mod || mod->type != mod_halflife) return -1; //halflife models only, please @@ -315,17 +451,23 @@ void HL_CalcBoneAdj(hlmodel_t *model) /*~~~~~~~~~~~~~~~~~~~~~*/ if(control[i].type & 0x8000) - { - value = model->controller[j] + control[i].start; + { //wraps normally + value = model->controller[j];// + control[i].start; } else { - value = (model->controller[j]+1)*0.5; //shifted to give a valid range between -1 and 1, with 0 being mid-range. - if(value < 0) - value = 0; - else if(value > 1.0) - value = 1.0; - value = (1.0 - value) * control[i].start + value * control[i].end; +// value = (model->controller[j]+1)*0.5; //shifted to give a valid range between -1 and 1, with 0 being mid-range. +// if(value < 0) +// value = 0; +// else if(value > 1.0) +// value = 1.0; +// value = (1.0 - value) * control[i].start + value * control[i].end; + + value = model->controller[j]; + if (value < control[i].start) + value = control[i].start; + if (value > control[i].end) + value = control[i].end; } /* Rotational controllers need their values converted */ @@ -342,11 +484,10 @@ void HL_CalcBoneAdj(hlmodel_t *model) ======================================================================================================================= */ void QuaternionSlerp( const vec4_t p, vec4_t q, float t, vec4_t qt ); -void HL_SetupBones(hlmodel_t *model, int seqnum, int firstbone, int lastbone, float subblendfrac, float frametime) +void HL_SetupBones(hlmodel_t *model, int seqnum, int firstbone, int lastbone, float subblendfrac, float frametime, float *matrix) { /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ int i; - float matrix[3][4]; vec3_t organg1[2]; vec3_t organg2[2]; vec3_t organgb[2]; @@ -362,6 +503,8 @@ void HL_SetupBones(hlmodel_t *model, int seqnum, int firstbone, int lastbone, fl hlmdl_anim_t *animation; /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + matrix += firstbone*12; + if (sequencedata->name[32]) { size_t fz; @@ -460,7 +603,7 @@ void HL_SetupBones(hlmodel_t *model, int seqnum, int firstbone, int lastbone, fl subblendfrac = 0; if (subblendfrac > 1) subblendfrac = 1; - for(i = firstbone; i < lastbone; i++) + for(i = firstbone; i < lastbone; i++, matrix+=12) { //calc first blend (writes organgb+quatb) HL_CalculateBones(frame1, model->adjust, model->bones + i, animation + i, organgb[0]); @@ -486,26 +629,17 @@ void HL_SetupBones(hlmodel_t *model, int seqnum, int firstbone, int lastbone, fl VectorInterpolate(organg1[0], frametime, organg2[0], organg1[0]); } - //blend the two + //blend the two, figure out a matrix. QuaternionSlerp(quatb, quat1, subblendfrac, quat1); - FloatInterpolate(organgb[0][0], subblendfrac, organg1[0][0], matrix[0][3]); - FloatInterpolate(organgb[0][0], subblendfrac, organg1[0][1], matrix[1][3]); - FloatInterpolate(organgb[0][0], subblendfrac, organg1[0][2], matrix[2][3]); - - /* If we have a parent, take the addition. Otherwise just copy the values */ - if(model->bones[i].parent>=0) - { - R_ConcatTransforms(transform_matrix[model->bones[i].parent], matrix, transform_matrix[i]); - } - else - { - memcpy(transform_matrix[i], matrix, 12 * sizeof(float)); - } + QuaternionGLMatrix(quat1[0], quat1[1], quat1[2], quat1[3], (vec4_t*)matrix); + FloatInterpolate(organgb[0][0], subblendfrac, organg1[0][0], matrix[0*4+3]); + FloatInterpolate(organgb[0][0], subblendfrac, organg1[0][1], matrix[1*4+3]); + FloatInterpolate(organgb[0][0], subblendfrac, organg1[0][2], matrix[2*4+3]); } } else { - for(i = firstbone; i < lastbone; i++) + for(i = firstbone; i < lastbone; i++, matrix+=12) { HL_CalculateBones(frame1, model->adjust, model->bones + i, animation + i, organg1[0]); QuaternionGLAngle(organg1[1], quat1); /* A quaternion */ @@ -519,147 +653,172 @@ void HL_SetupBones(hlmodel_t *model, int seqnum, int firstbone, int lastbone, fl VectorInterpolate(organg1[0], frametime, organg2[0], organg1[0]); } + //figure out the relative bone matrix. //we probably ought to keep them as quats or something. - QuaternionGLMatrix(quat1[0], quat1[1], quat1[2], quat1[3], matrix); - matrix[0][3] = organg1[0][0]; - matrix[1][3] = organg1[0][1]; - matrix[2][3] = organg1[0][2]; - - /* If we have a parent, take the addition. Otherwise just copy the values */ - if(model->bones[i].parent>=0) - { - R_ConcatTransforms(transform_matrix[model->bones[i].parent], matrix, transform_matrix[i]); - } - else - { - memcpy(transform_matrix[i], matrix, 12 * sizeof(float)); - } + QuaternionGLMatrix(quat1[0], quat1[1], quat1[2], quat1[3], (vec4_t*)matrix); + matrix[0*4+3] = organg1[0][0]; + matrix[1*4+3] = organg1[0][1]; + matrix[2*4+3] = organg1[0][2]; } } } -void R_HL_BuildFrame(hlmodel_t *model, hlmdl_model_t *amodel, entity_t *curent, short *order, float tex_s, float tex_t, mesh_t *mesh) +int HLMDL_GetNumBones(model_t *mod) { - static vecV_t xyz[2048]; - static vec3_t norm[2048]; - static vec2_t st[2048]; - static byte_vec4_t vc[2048]; - static index_t index[4096]; - int count; - int b; - int cbone; - int bgroup; - int lastbone; - int v, i; - vec3_t *verts; - qbyte *bone; - vec3_t transformed[2048]; + hlmodel_t *mc; + if (!mod || mod->type != mod_halflife) + return -1; //halflife models only, please - int idx = 0; - int vert = 0; + mc = Mod_Extradata(mod); + return mc->header->numbones; +} - mesh->xyz_array = xyz; - mesh->st_array = st; - mesh->normals_array = norm; //for lighting algos to not crash - mesh->snormals_array = norm; //for rtlighting - mesh->tnormals_array = norm; //for rtlighting - mesh->indexes = index; - mesh->colors4b_array = vc; +int HLMDL_GetBoneData(model_t *mod, int firstbone, int lastbone, framestate_t *fstate, float *result) +{ + int b, cbone, bgroup; + hlmodel_t *model = Mod_Extradata(mod); for (b = 0; b < MAX_BONE_CONTROLLERS; b++) - model->controller[b] = curent->framestate.bonecontrols[b]; - -// Con_Printf("%s %i\n", sequence->name, sequence->unknown1[0]); - - cbone = 0; - for (bgroup = 0; bgroup < FS_COUNT; bgroup++) + model->controller[b] = fstate->bonecontrols[b]; + for (cbone = 0, bgroup = 0; bgroup < FS_COUNT; bgroup++) { - lastbone = curent->framestate.g[bgroup].endbone; + lastbone = fstate->g[bgroup].endbone; if (bgroup == FS_COUNT-1) lastbone = model->header->numbones; if (cbone >= lastbone) continue; - HL_SetupBones(model, curent->framestate.g[bgroup].frame[0], cbone, lastbone, (curent->framestate.g[bgroup].subblendfrac+1)*0.5, curent->framestate.g[bgroup].frametime[0]); /* Setup the bones */ + HL_SetupBones(model, fstate->g[bgroup].frame[0], cbone, lastbone, (fstate->g[bgroup].subblendfrac+1)*0.5, fstate->g[bgroup].frametime[0], result); /* Setup the bones */ cbone = lastbone; } + return cbone; +} +const char *HLMDL_FrameNameForNum(model_t *mod, int surfaceidx, int seqnum) +{ + hlmodel_t *model = Mod_Extradata(mod); + hlmdl_sequencelist_t *sequence = (hlmdl_sequencelist_t *) ((qbyte *) model->header + model->header->seqindex) + + ((unsigned int)seqnum>=model->header->numseq?0:seqnum); + return sequence->name; +} +qboolean HLMDL_FrameInfoForNum(model_t *mod, int surfaceidx, int seqnum, char **name, int *numframes, float *duration, qboolean *loop) +{ + hlmodel_t *model = Mod_Extradata(mod); + hlmdl_sequencelist_t *sequence = (hlmdl_sequencelist_t *) ((qbyte *) model->header + model->header->seqindex) + + ((unsigned int)seqnum>=model->header->numseq?0:seqnum); - verts = (vec3_t *) ((qbyte *) model->header + amodel->vertindex); - bone = ((qbyte *) model->header + amodel->vertinfoindex); - for(v = 0; v < amodel->numverts; v++) + *name = sequence->name; + *numframes = sequence->numframes; + *duration = sequence->numframes/sequence->timing; + *loop = sequence->loop; + return true; +} + +void R_HL_BuildFrame(hlmodel_t *model, hlmdl_submodel_t *amodel, entity_t *curent, int bodypart, int bodyidx, int meshidx, float tex_s, float tex_t, mesh_t *mesh, qboolean gpubones) +{ + int b; + int cbone; + int bgroup; + int lastbone; + int v; + + *mesh = model->geomset[bodypart].alternatives[bodyidx].mesh; + + //FIXME: cache this! + if (curent->framestate.bonecount >= model->header->numbones) { - VectorTransform(verts[v], (void *)transform_matrix[bone[v]], transformed[v]); - } - - for(;;) - { - count = *order++; /* get the vertex count and primitive type */ - if(!count) break; /* done */ - - if(count < 0) + if (curent->framestate.skeltype == SKEL_RELATIVE) { - count = -count; - - //emit (count-2)*3 indicies as a fan - - - - for (i = 0; i < count-2; i++) + mesh->numbones = model->header->numbones; + for (b = 0; b < mesh->numbones; b++) { - index[idx++] = vert + 0; - index[idx++] = vert + i+1; - index[idx++] = vert + i+2; + /* If we have a parent, take the addition. Otherwise just copy the values */ + if(model->bones[b].parent>=0) + { + R_ConcatTransforms(transform_matrix[model->bones[b].parent], (void*)(curent->framestate.bonestate+b*12), transform_matrix[b]); + } + else + { + memcpy(transform_matrix[b], curent->framestate.bonestate+b*12, 12 * sizeof(float)); + } } + mesh->bones = transform_matrix[0][0]; } else { - //emit (count-2)*3 indicies as a strip + mesh->bones = curent->framestate.bonestate; + mesh->numbones = curent->framestate.bonecount; + } + } + else + { + float relatives[12*MAX_BONES]; + mesh->bones = transform_matrix[0][0]; + mesh->numbones = model->header->numbones; - for (i = 0; ; ) - { - if (i == count-2) - break; - index[idx++] = vert + i; - index[idx++] = vert + i+1; - index[idx++] = vert + i+2; - i++; - - if (i == count-2) - break; - index[idx++] = vert + i; - index[idx++] = vert + i+2; - index[idx++] = vert + i+1; - i++; - } + //FIXME: needs caching. + for (b = 0; b < MAX_BONE_CONTROLLERS; b++) + model->controller[b] = curent->framestate.bonecontrols[b]; + for (cbone = 0, bgroup = 0; bgroup < FS_COUNT; bgroup++) + { + lastbone = curent->framestate.g[bgroup].endbone; + if (bgroup == FS_COUNT-1) + lastbone = model->header->numbones; + if (cbone >= lastbone) + continue; + HL_SetupBones(model, curent->framestate.g[bgroup].frame[0], cbone, lastbone, (curent->framestate.g[bgroup].subblendfrac+1)*0.5, curent->framestate.g[bgroup].frametime[0], relatives); /* Setup the bones */ + cbone = lastbone; } - do + //convert relative to absolutes + for (b = 0; b < cbone; b++) { - VectorCopy(transformed[order[0]], xyz[vert]); - - //FIXME: what's order[1]? - - /* texture coordinates come from the draw list */ - st[vert][0] = order[2] * tex_s; - st[vert][1] = order[3] * tex_t; - - /*fixme: build vertex normals in the base pose and transform them using the same bone matricies (just discard the origin part)*/ - norm[vert][0] = 1; - norm[vert][1] = 1; - norm[vert][2] = 1; - - vc[vert][0] = 255; - vc[vert][1] = 255; - vc[vert][2] = 255; - vc[vert][3] = 255; - - order += 4; - vert++; - } while(--count); + /* If we have a parent, take the addition. Otherwise just copy the values */ + if(model->bones[b].parent>=0) + { + R_ConcatTransforms(transform_matrix[model->bones[b].parent], (void*)(relatives+b*12), transform_matrix[b]); + } + else + { + memcpy(transform_matrix[b], relatives+b*12, 12 * sizeof(float)); + } + } } - mesh->numindexes = idx; - mesh->numvertexes = vert; + mesh->indexes += model->geomset[bodypart].alternatives[bodyidx].submesh[meshidx].firstindex; + mesh->numindexes = model->geomset[bodypart].alternatives[bodyidx].submesh[meshidx].numindexes; + + if (gpubones) + { //get the backend to do the skeletal stuff (read: glsl) + for(v = 0; v < mesh->numvertexes; v++) + { //should really come up with a better way to deal with this, like rect textures. + mesh->st_array[v][0] = mesh->lmst_array[0][v][0] * tex_s; + mesh->st_array[v][1] = mesh->lmst_array[0][v][1] * tex_t; + } + } + else + { //backend can't handle it, apparently. do it in software. + static vecV_t nxyz[2048]; + static vec3_t nnorm[2048]; + for(v = 0; v < mesh->numvertexes; v++) + { //should really come up with a better way to deal with this, like rect textures. + mesh->st_array[v][0] = mesh->lmst_array[0][v][0] * tex_s; + mesh->st_array[v][1] = mesh->lmst_array[0][v][1] * tex_t; + + VectorTransform(mesh->xyz_array[v], (void *)transform_matrix[mesh->bonenums[v][0]], nxyz[v]); + + nnorm[v][0] = DotProduct(mesh->normals_array[v], transform_matrix[mesh->bonenums[v][0]][0]); + nnorm[v][1] = DotProduct(mesh->normals_array[v], transform_matrix[mesh->bonenums[v][0]][1]); + nnorm[v][2] = DotProduct(mesh->normals_array[v], transform_matrix[mesh->bonenums[v][0]][2]); + + //FIXME: svector, tvector! + } + mesh->xyz_array = nxyz; + mesh->normals_array = nnorm; + mesh->bonenums = NULL; + mesh->boneweights = NULL; + mesh->bones = NULL; + mesh->numbones = 0; + } } void R_HalfLife_WalkMeshes(entity_t *rent, batch_t *b, batch_t **batches); @@ -670,26 +829,24 @@ void R_HL_BuildMesh(struct batch_s *b) void R_HalfLife_WalkMeshes(entity_t *rent, batch_t *b, batch_t **batches) { - hlmodelcache_t *modelc = Mod_Extradata(rent->model); - hlmodel_t model; + hlmodel_t *model = Mod_Extradata(rent->model); int body, m; int batchid = 0; static mesh_t bmesh, *mptr = &bmesh; + skinfile_t *sk = NULL; - //general model - model.header = modelc->header; - model.bones = modelc->bones; - model.bonectls = modelc->bonectls; - model.shaders = modelc->shaders; - model.animcache = modelc->animcache; - model.memgroup = &rent->model->memgroup; + unsigned int entity_body = rent->bottomcolour; - for (body = 0; body < model.header->numbodyparts; body++) + if (rent->customskin) + sk = Mod_LookupSkin(rent->customskin); + //entity_body = rent->body; //hey, if its there, lets use it. + + for (body = 0; body < model->header->numbodyparts; body++) { /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ - hlmdl_bodypart_t *bodypart = (hlmdl_bodypart_t *) ((qbyte *) model.header + model.header->bodypartindex) + body; - int bodyindex = (0 / bodypart->base) % bodypart->nummodels; - hlmdl_model_t *amodel = (hlmdl_model_t *) ((qbyte *) model.header + bodypart->modelindex) + bodyindex; + hlmdl_bodypart_t *bodypart = (hlmdl_bodypart_t *) ((qbyte *) model->header + model->header->bodypartindex) + body; + int bodyindex = ((sk && body < MAX_GEOMSETS && sk->geomset[body] >= 1)?sk->geomset[body]-1:(entity_body / bodypart->base)) % bodypart->nummodels; + hlmdl_submodel_t *amodel = (hlmdl_submodel_t *) ((qbyte *) model->header + bodypart->modelindex) + bodyindex; /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ @@ -697,16 +854,18 @@ void R_HalfLife_WalkMeshes(entity_t *rent, batch_t *b, batch_t **batches) for(m = 0; m < amodel->nummesh; m++) { /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ - hlmdl_mesh_t *mesh = (hlmdl_mesh_t *) ((qbyte *) model.header + amodel->meshindex) + m; + hlmdl_mesh_t *mesh = (hlmdl_mesh_t *) ((qbyte *) model->header + amodel->meshindex) + m; float tex_w; float tex_h; struct hlmodelshaders_s *s; + int skinidx = mesh->skinindex; /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ - if (mesh->skinindex >= modelc->numskins) - continue; - - s = &model.shaders[modelc->skins[mesh->skinindex]]; + if (skinidx >= model->numskinrefs) + continue; //can happen from bad mesh/skin mixing + if (rent->skinnum < model->numskingroups) + skinidx += rent->skinnum * model->numskinrefs; + s = &model->shaders[model->skinref[skinidx]]; if (batches) { @@ -716,19 +875,34 @@ void R_HalfLife_WalkMeshes(entity_t *rent, batch_t *b, batch_t **batches) if (!b) return; + if (!s->shader) { s->shader = R_RegisterSkin(s->name, rent->model->name); R_BuildDefaultTexnums(&s->defaulttex, s->shader); } + b->skin = NULL; + b->shader = s->shader; + if (sk) + { + int i; + for (i = 0; i < sk->nummappings; i++) + { + if (!strcmp(sk->mappings[i].surface, s->name)) + { + b->skin = &sk->mappings[i].texnums; + b->shader = sk->mappings[i].shader; + break; + } + } + } + b->buildmeshes = R_HL_BuildMesh; b->ent = rent; b->mesh = NULL; b->firstmesh = 0; b->meshes = 1; - b->skin = NULL; b->texture = NULL; - b->shader = s->shader; for (j = 0; j < MAXRLIGHTMAPS; j++) b->lightmap[j] = -1; b->surf_first = batchid; @@ -761,7 +935,7 @@ void R_HalfLife_WalkMeshes(entity_t *rent, batch_t *b, batch_t **batches) b->next = batches[sort]; batches[sort] = b; } - else + else { if (batchid == b->surf_first) { @@ -769,7 +943,7 @@ void R_HalfLife_WalkMeshes(entity_t *rent, batch_t *b, batch_t **batches) tex_h = 1.0f / s->h; b->mesh = &mptr; - R_HL_BuildFrame(&model, amodel, b->ent, (short *) ((qbyte *) model.header + mesh->index), tex_w, tex_h, b->mesh[0]); + R_HL_BuildFrame(model, amodel, b->ent, body, bodyindex, m, tex_w, tex_h, b->mesh[0], b->shader->prog && (b->shader->prog->supportedpermutations & PERMUTATION_SKELETAL)); return; } } diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index 3401d04b..f475b3ee 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -452,13 +452,48 @@ void Mod_ResortShaders(void) const char *Mod_GetEntitiesString(model_t *mod) { + size_t vl; + size_t e; + size_t sz; + char *o; if (!mod) return NULL; if (mod->entities_raw) //still cached/correct return mod->entities_raw; if (!mod->numentityinfo) return NULL; + //reform the entities back into a full string now that we apparently need it + //find needed buffer size + for (e = 0, sz = 0; e < mod->numentityinfo; e++) + { + if (!mod->entityinfo[e].keyvals) + continue; + sz += 2; + sz += strlen(mod->entityinfo[e].keyvals); + sz += 2; + } + sz+=1; + o = BZ_Malloc(sz); + + //splurge it out + for (e = 0, sz = 0; e < mod->numentityinfo; e++) + { + if (!mod->entityinfo[e].keyvals) + continue; + o[sz+0] = '{'; + o[sz+1] = '\n'; + sz += 2; + vl = strlen(mod->entityinfo[e].keyvals); + memcpy(&o[sz], mod->entityinfo[e].keyvals, vl); + sz += vl; + o[sz+0] = '}'; + o[sz+1] = '\n'; + sz += 2; + } + o[sz+0] = 0; + + mod->entities_raw = o; return mod->entities_raw; } void Mod_SetEntitiesString(model_t *mod, const char *str, qboolean docopy) @@ -493,6 +528,64 @@ void Mod_SetEntitiesStringLen(model_t *mod, const char *str, size_t strsize) Mod_SetEntitiesString(mod, str, false); } +void Mod_ParseEntities(model_t *mod) +{ + char key[1024]; + char value[4096]; + const char *entstart; + const char *entend; + const char *entdata; + size_t c, m; + + c = 0; m = 0; + + while (mod->numentityinfo > 0) + Z_Free(mod->entityinfo[--mod->numentityinfo].keyvals); + Z_Free(mod->entityinfo); + mod->entityinfo = NULL; + + + entdata = mod->entities_raw; + while(1) + { + if (!(entdata=COM_ParseOut(entdata, key, sizeof(key)))) + break; + if (strcmp(key, "{")) + break; + + //skip whitespace to save space. + while (*entdata == ' ' || *entdata == '\r' || *entdata == '\n' || *entdata == '\t') + entdata++; + + entstart = entdata; + + while(1) + { + entend = entdata; + entdata=COM_ParseOut(entdata, key, sizeof(key)); + if (!strcmp(key, "}")) + break; + entdata=COM_ParseOut(entdata, value, sizeof(value)); + } + if (!entdata) + break; //erk. eof + + if (c == m) + { + if (!m) + m = 64; + else + m *= 2; + mod->entityinfo = BZ_Realloc(mod->entityinfo, sizeof(*mod->entityinfo) * m); + } + mod->entityinfo[c].keyvals = BZ_Malloc(entend-entstart + 1); + memcpy(mod->entityinfo[c].keyvals, entstart, entend-entstart); + mod->entityinfo[c].keyvals[entend-entstart] = 0; + c++; + } + mod->numentityinfo = c; +} + /* =================== Mod_ClearAll diff --git a/engine/gl/gl_rmain.c b/engine/gl/gl_rmain.c index 2b37c2c3..4e5d777c 100644 --- a/engine/gl/gl_rmain.c +++ b/engine/gl/gl_rmain.c @@ -899,9 +899,6 @@ void GLR_DrawPortal(batch_t *batch, batch_t **blist, batch_t *depthmasklist[2], qbyte newvis[(MAX_MAP_LEAFS+7)/8]; float ivmat[16], trmat[16]; - if (r_refdef.recurse >= R_MAX_RECURSE-1) - return; - if (!mesh->xyz_array) return; @@ -947,6 +944,15 @@ void GLR_DrawPortal(batch_t *batch, batch_t **blist, batch_t *depthmasklist[2], if (DotProduct(r_refdef.vieworg, plane.normal)-plane.dist < -r_refdef.mindist) return; + if (r_refdef.recurse >= R_MAX_RECURSE-1) + { + GLBE_SelectMode(BEM_DEPTHDARK); + GLBE_SubmitBatch(batch); + GLBE_SelectMode(BEM_STANDARD); + return; + } + + TRACE(("GLR_DrawPortal: portal type %i\n", portaltype)); oldrefdef = r_refdef; @@ -1156,9 +1162,7 @@ void GLR_DrawPortal(batch_t *batch, batch_t **blist, batch_t *depthmasklist[2], fp.dist += 0.01; r_refdef.frustum[r_refdef.frustum_numplanes++] = fp; } - - //force culling to update to match the new front face. -// memcpy(r_refdef.m_view, vmat, sizeof(float)*16); +#if 1 if (depthmasklist) { /*draw already-drawn portals as depth-only, to ensure that their contents are not harmed*/ @@ -1172,7 +1176,6 @@ void GLR_DrawPortal(batch_t *batch, batch_t **blist, batch_t *depthmasklist[2], qglMatrixMode(GL_MODELVIEW); } //portals to mask are relative to the old view still. - GLBE_SelectEntity(&r_worldentity); currententity = NULL; if (gl_config.arb_depth_clamp) qglEnable(GL_DEPTH_CLAMP_ARB); //ignore the near clip plane(ish), this means nearer portals can still mask further ones. @@ -1184,8 +1187,8 @@ void GLR_DrawPortal(batch_t *batch, batch_t **blist, batch_t *depthmasklist[2], { if (dmask == batch) continue; - if (dmask->meshes == dmask->firstmesh) - continue; +// if (dmask->meshes == dmask->firstmesh) +// continue; GLBE_SubmitBatch(dmask); } } @@ -1195,6 +1198,9 @@ void GLR_DrawPortal(batch_t *batch, batch_t **blist, batch_t *depthmasklist[2], currententity = NULL; } +#endif +// r_refdef = oldrefdef; +// return; //now determine the stuff the backend will use. memcpy(r_refdef.m_view, vmat, sizeof(float)*16); diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 4c7e3bfc..6c4d1e4b 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -221,9 +221,11 @@ enum shaderparsemode_e static struct { enum shaderparsemode_e mode; + qboolean droppass; qboolean forceprogramify; //for dpwater compat, used to generate a program + int dpwatertype; float reflectmin; float reflectmax; float reflectfactor; @@ -231,7 +233,6 @@ static struct vec3_t refractcolour; vec3_t reflectcolour; float wateralpha; - qboolean droppass; } parsestate; typedef struct shaderkey_s @@ -2281,6 +2282,7 @@ static void Shader_DP_Water(shader_t *shader, shaderpass_t *pass, char **ptr) { parsestate.forceprogramify = true; + parsestate.dpwatertype |= 3; parsestate.reflectmin = Shader_ParseFloat(shader, ptr, 0); parsestate.reflectmax = Shader_ParseFloat(shader, ptr, 0); parsestate.refractfactor = Shader_ParseFloat(shader, ptr, 0); @@ -2293,6 +2295,9 @@ static void Shader_DP_Reflect(shader_t *shader, shaderpass_t *pass, char **ptr) { parsestate.forceprogramify = true; + parsestate.dpwatertype |= 1; + parsestate.reflectmin = 1; + parsestate.reflectmax = 1; parsestate.reflectfactor = Shader_ParseFloat(shader, ptr, 0); Shader_ParseVector(shader, ptr, parsestate.reflectcolour); } @@ -2300,6 +2305,7 @@ static void Shader_DP_Refract(shader_t *shader, shaderpass_t *pass, char **ptr) { parsestate.forceprogramify = true; + parsestate.dpwatertype |= 2; parsestate.refractfactor = Shader_ParseFloat(shader, ptr, 0); Shader_ParseVector(shader, ptr, parsestate.refractcolour); } @@ -4257,9 +4263,9 @@ void Shader_Programify (shader_t *s) return;*/ } - if (parsestate.refractfactor || parsestate.reflectfactor) + if (parsestate.dpwatertype) { - prog = va("altwater#REFLECT#USEMODS#FRESNEL_EXP=2.0" + prog = va("altwater%s#USEMODS#FRESNEL_EXP=2.0" //variable parts "#STRENGTH_REFR=%g#STRENGTH_REFL=%g" "#TINT_REFR=%g,%g,%g" @@ -4267,6 +4273,7 @@ void Shader_Programify (shader_t *s) "#FRESNEL_MIN=%g#FRESNEL_RANGE=%g" "#ALPHA=%g", //those args + (parsestate.dpwatertype&1)?"#REFLECT":"", parsestate.refractfactor*0.01, parsestate.reflectfactor*0.01, parsestate.refractcolour[0],parsestate.refractcolour[1],parsestate.refractcolour[2], parsestate.reflectcolour[0],parsestate.reflectcolour[1],parsestate.reflectcolour[2], @@ -5633,7 +5640,7 @@ void Shader_DefaultBSPQ2(const char *shortname, shader_t *s, const void *args) void Shader_DefaultBSPQ1(const char *shortname, shader_t *s, const void *args) { char *builtin = NULL; - if (r_mirroralpha.value < 1 && !strcmp(shortname, "window02_1")) + if (r_mirroralpha.value < 1 && (!strcmp(shortname, "window02_1") || !strncmp(shortname, "mirror", 6))) { if (r_mirroralpha.value < 0) { diff --git a/engine/gl/model_hl.h b/engine/gl/model_hl.h index 9641de37..75c561cf 100644 --- a/engine/gl/model_hl.h +++ b/engine/gl/model_hl.h @@ -21,28 +21,30 @@ */ typedef struct { - int filetypeid; //IDSP + int filetypeid; //IDSP int version; //10 - char name[64]; - int filesize; - vec3_t unknown3[5]; - int unknown4; - int numbones; - int boneindex; - int numcontrollers; - int controllerindex; - int unknown5[2]; - int numseq; - int seqindex; - int unknown6; - int seqgroups; - int numtextures; - int textures; - int unknown7[3]; - int skins; - int numbodyparts; - int bodypartindex; - int unknown9[8]; + char name[64]; + int filesize; + vec3_t unknown3[5]; + int unknown4; //flags + int numbones; + int boneindex; + int numcontrollers; + int controllerindex; + int unknown5[2]; //hitboxes + int numseq; + int seqindex; + int unknown6; //external sequences + int seqgroups; + int numtextures; + int textures; + int unknown7; //something to do with external textures + int skinrefs; + int skingroups; + int skins; + int numbodyparts; + int bodypartindex; + int unknown9[8]; //attachments, sounds, transitions } hlmdl_header_t; /* @@ -52,11 +54,11 @@ typedef struct */ typedef struct { - char name[64]; - int flags; - int w; /* width */ - int h; /* height */ - int offset; /* index */ + char name[64]; + int flags; /*flat, chrome, fullbright*/ + int w; /* width */ + int h; /* height */ + int offset; /* index */ } hlmdl_tex_t; /* @@ -66,10 +68,10 @@ typedef struct */ typedef struct { - char name[64]; - int nummodels; - int base; - int modelindex; + char name[64]; + int nummodels; + int base; + int modelindex; } hlmdl_bodypart_t; /* @@ -79,11 +81,11 @@ typedef struct */ typedef struct { - int numtris; - int index; - int skinindex; - int unknown2; - int unknown3; + int numtris; + int index; + int skinindex; + int unknown2; + int unknown3; } hlmdl_mesh_t; /* @@ -93,12 +95,12 @@ typedef struct */ typedef struct { - char name[32]; - int parent; - int unknown1; - int bonecontroller[6]; - float value[6]; - float scale[6]; + char name[32]; + int parent; + int unknown1; + int bonecontroller[6]; + float value[6]; + float scale[6]; } hlmdl_bone_t; /* @@ -108,31 +110,33 @@ typedef struct */ typedef struct { - int name; - int type; - float start; - float end; - int unknown1; - int index; + int name; + int type; + float start; + float end; + int unknown1; + int index; } hlmdl_bonecontroller_t; /* ----------------------------------------------------------------------------------------------------------------------- - halflife model descriptor + halflife submodel descriptor ----------------------------------------------------------------------------------------------------------------------- */ typedef struct { - char name[64]; - int unknown1; - float unknown2; - int nummesh; - int meshindex; - int numverts; - int vertinfoindex; - int vertindex; - int unknown3[5]; -} hlmdl_model_t; + char name[64]; + int unknown1; + float unknown2; + int nummesh; + int meshindex; + int numverts; + int vertinfoindex; + int vertindex; + int unknown3[2]; + int normindex; + int unknown4[2]; +} hlmdl_submodel_t; /* ----------------------------------------------------------------------------------------------------------------------- @@ -141,7 +145,7 @@ typedef struct */ typedef struct { - unsigned short offset[6]; + unsigned short offset[6]; } hlmdl_anim_t; /* @@ -151,11 +155,11 @@ typedef struct */ typedef union { - struct { - qbyte valid; - qbyte total; - } num; - short value; + struct { + qbyte valid; + qbyte total; + } num; + short value; } hlmdl_animvalue_t; /* @@ -165,24 +169,24 @@ typedef union */ typedef struct { - char name[32]; - float timing; + char name[32]; + float timing; int loop; - int unknown1[4]; - int numframes; - int unknown2[2]; - int motiontype; - int motionbone; - vec3_t unknown3; - int unknown4[2]; - vec3_t bbox[2]; - int hasblendseq; - int index; - int unknown7[2]; - float unknown[4]; - int unknown8; - unsigned int seqindex; - int unknown9[4]; + int unknown1[4]; + int numframes; + int unknown2[2]; + int motiontype; + int motionbone; + vec3_t unknown3; + int unknown4[2]; + vec3_t bbox[2]; + int hasblendseq; + int index; + int unknown7[2]; + float unknown[4]; + int unknown8; + unsigned int seqindex; + int unknown9[4]; } hlmdl_sequencelist_t; /* @@ -192,9 +196,9 @@ typedef struct */ typedef struct { - char name[96]; /* should be split label[32] and name[64] */ - unsigned int cache; - int data; + char name[96]; /* should be split label[32] and name[64] */ + unsigned int cache; + int data; } hlmdl_sequencedata_t; typedef struct @@ -209,27 +213,19 @@ typedef struct halflife model internal structure ----------------------------------------------------------------------------------------------------------------------- */ -typedef struct -{ - float controller[5]; /* Position of bone controllers */ - float adjust[5]; - - /* Static pointers */ - hlmdl_header_t *header; - hlmdl_bone_t *bones; - hlmdl_bonecontroller_t *bonectls; - struct hlmodelshaders_s *shaders; - hlmdl_sequencefile_t **animcache; - zonegroup_t *memgroup; -} hlmodel_t; #define MAX_ANIM_GROUPS 16 //submodel files containing anim data. typedef struct //this is stored as the cache. an hlmodel_t is generated when drawing { - hlmdl_header_t *header; - hlmdl_bone_t *bones; - hlmdl_bonecontroller_t *bonectls; + //updated while rendering... + float controller[5]; /* Position of bone controllers */ + float adjust[5]; + + hlmdl_header_t *header; + hlmdl_bone_t *bones; + hlmdl_bonecontroller_t *bonectls; hlmdl_sequencefile_t *animcache[MAX_ANIM_GROUPS]; + zonegroup_t *memgroup; struct hlmodelshaders_s { char name[MAX_QPATH]; @@ -237,9 +233,26 @@ typedef struct //this is stored as the cache. an hlmodel_t is generated when dra shader_t *shader; int w, h; } *shaders; - short *skins; - int numskins; -} hlmodelcache_t; + short *skinref; + int numskinrefs; + int numskingroups; + + int numgeomsets; + struct + { + int numalternatives; + struct hlalternative_s + { + mesh_t mesh; + int numsubmeshes; + struct + { + int firstindex; + int numindexes; + } *submesh; + } *alternatives; + } *geomset; +} hlmodel_t; /* HL mathlib prototypes: */ void QuaternionGLAngle(const vec3_t angles, vec4_t quaternion); @@ -252,3 +265,11 @@ void R_DrawHLModel(entity_t *curent); /* physics stuff */ void *Mod_GetHalfLifeModelData(model_t *mod); + +//reflectioney things, including bone data +int HLMDL_BoneForName(model_t *mod, const char *name); +int HLMDL_FrameForName(model_t *mod, const char *name); +const char *HLMDL_FrameNameForNum(model_t *model, int surfaceidx, int num); +qboolean HLMDL_FrameInfoForNum(model_t *model, int surfaceidx, int num, char **name, int *numframes, float *duration, qboolean *loop); +int HLMDL_GetNumBones(model_t *mod); +int HLMDL_GetBoneData(model_t *model, int firstbone, int lastbone, framestate_t *fstate, float *result); diff --git a/engine/gl/r_bishaders.h b/engine/gl/r_bishaders.h index b5b66568..4bbb5cd6 100644 --- a/engine/gl/r_bishaders.h +++ b/engine/gl/r_bishaders.h @@ -3121,6 +3121,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND #endif #ifdef D3D11QUAKE {QR_DIRECT3D11, 11, "defaultskin", +"!!permu UPPERLOWER\n" "!!samps diffuse upper lower fullbright\n" "struct a2v\n" @@ -3183,11 +3184,11 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#ifdef UPPER\n" "float4 uc = t_upper.Sample(SampleType, inp.tc);\n" -"col.rgb = lerp(col.rgb, uc.rgb*e_uppercolour, uc.a);\n" +"col.rgb += uc.rgb*e_uppercolour.rgb*uc.a;\n" "#endif\n" "#ifdef LOWER\n" "float4 lc = t_lower.Sample(SampleType, inp.tc);\n" -"col.rgb = lerp(col.rgb, lc.rgb*e_lowercolour, lc.a);\n" +"col.rgb += lc.rgb*e_lowercolour.rgb*lc.a;\n" "#endif\n" "col.rgb *= inp.light;\n" //#ifdef FULLBRIGHT @@ -4482,7 +4483,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND #endif #ifdef GLQUAKE {QR_OPENGL, 110, "defaultwall", -"!!ver 110 130\n" +"!!ver 110 // 130\n" "!!permu DELUXE\n" "!!permu FULLBRIGHT\n" "!!permu FOG\n" diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index f3c36974..1f8f6848 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -11939,8 +11939,12 @@ void PR_DumpPlatform_f(void) {"TEREDIT_RESET_SECT", "const float", CS, NULL, ter_reset}, {"TEREDIT_RELOAD_SECT", "const float", CS, NULL, ter_reloadsect}, {"TEREDIT_ENTS_WIPE", "const float", CS, NULL, ter_ents_wipe}, - {"TEREDIT_ENTS_CONCAT", "const float", CS, NULL, ter_ents_concat}, - {"TEREDIT_ENTS_GET", "const float", CS, NULL, ter_ents_get}, +// {"TEREDIT_ENTS_CONCAT", "const float", CS, NULL, ter_ents_concat}, +// {"TEREDIT_ENTS_GET", "const float", CS, NULL, ter_ents_get}, + {"TEREDIT_ENT_GET", "const float", CS, NULL, ter_ent_get}, + {"TEREDIT_ENT_SET", "const float", CS, NULL, ter_ent_set}, + {"TEREDIT_ENT_ADD", "const float", CS, NULL, ter_ent_add}, + {"TEREDIT_ENT_COUNT", "const float", CS, NULL, ter_ent_count}, #endif {"SLIST_HOSTCACHEVIEWCOUNT", "const float", CS|MENU, NULL, SLIST_HOSTCACHEVIEWCOUNT}, diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 68240f48..18481aa1 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -6398,7 +6398,7 @@ int SV_PMTypeForClient (client_t *cl, edict_t *ent) case MOVETYPE_WALK: default: #ifndef NOLEGACY - if (ent->v->health <= 0) + if (cl && ent->v->health <= 0) return PM_DEAD; #endif return PM_NORMAL; diff --git a/engine/shaders/glsl/defaultwall.glsl b/engine/shaders/glsl/defaultwall.glsl index 84ad91e5..cb3ef694 100644 --- a/engine/shaders/glsl/defaultwall.glsl +++ b/engine/shaders/glsl/defaultwall.glsl @@ -1,4 +1,4 @@ -!!ver 110 130 +!!ver 110 // 130 !!permu DELUXE !!permu FULLBRIGHT !!permu FOG diff --git a/engine/shaders/hlsl11/defaultskin.hlsl b/engine/shaders/hlsl11/defaultskin.hlsl index d066d36c..c2d12d31 100644 --- a/engine/shaders/hlsl11/defaultskin.hlsl +++ b/engine/shaders/hlsl11/defaultskin.hlsl @@ -1,3 +1,4 @@ +!!permu UPPERLOWER !!samps diffuse upper lower fullbright struct a2v @@ -60,11 +61,11 @@ struct v2f #ifdef UPPER float4 uc = t_upper.Sample(SampleType, inp.tc); - col.rgb = lerp(col.rgb, uc.rgb*e_uppercolour, uc.a); + col.rgb += uc.rgb*e_uppercolour.rgb*uc.a; #endif #ifdef LOWER float4 lc = t_lower.Sample(SampleType, inp.tc); - col.rgb = lerp(col.rgb, lc.rgb*e_lowercolour, lc.a); + col.rgb += lc.rgb*e_lowercolour.rgb*lc.a; #endif col.rgb *= inp.light; //#ifdef FULLBRIGHT diff --git a/engine/vk/vk_backend.c b/engine/vk/vk_backend.c index 6fb5d325..c077e6ae 100644 --- a/engine/vk/vk_backend.c +++ b/engine/vk/vk_backend.c @@ -6051,10 +6051,9 @@ void VKBE_DrawWorld (batch_t **worldbatches) //fixme: figure out some way to safely orphan this data so that we can throw the rest to a worker. BE_GenModelBatches(batches, shaderstate.curdlight, BEM_STANDARD); + BE_UploadLightmaps(false); if (r_refdef.scenevis) { - BE_UploadLightmaps(false); - //make sure the world draws correctly r_worldentity.shaderRGBAf[0] = 1; r_worldentity.shaderRGBAf[1] = 1; diff --git a/engine/vk/vk_init.c b/engine/vk/vk_init.c index bd01b316..a75f937a 100644 --- a/engine/vk/vk_init.c +++ b/engine/vk/vk_init.c @@ -1352,7 +1352,7 @@ static qboolean VK_R_RenderScene_Cubemap(struct vk_rendertarg *fb) shader_t *shader; int facemask; extern cvar_t r_projection; - int osm = r_refdef.stereomethod; + int osm; struct vk_rendertarg_cube *rtc = &vk_rt_cubemap; if (!*ffov.string || !strcmp(ffov.string, "0")) @@ -1497,6 +1497,7 @@ static qboolean VK_R_RenderScene_Cubemap(struct vk_rendertarg *fb) VectorCopy(r_refdef.viewangles, saveang); saveang[2] = 0; + osm = r_refdef.stereomethod; r_refdef.stereomethod = STEREO_OFF; VKBE_RT_Gen_Cube(rtc, cmapsize, r_clear.ival?true:false); diff --git a/quakec/csaddon/src/csaddon.src b/quakec/csaddon/src/csaddon.src index 8565fda9..1c9dc88b 100644 --- a/quakec/csaddon/src/csaddon.src +++ b/quakec/csaddon/src/csaddon.src @@ -1,6 +1,10 @@ ../csaddon.dat //pr_dumpplatform -FFTE -Fdefines -TCS -O csplat -opts.qc + +//#pragma flag enable assumeint +//#pragma flag enable typeexplicit + +#define CSQC csplat.qc csfixups.qc diff --git a/quakec/csaddon/src/csplat.qc b/quakec/csaddon/src/csplat.qc index 6e95a6b6..395659e6 100644 --- a/quakec/csaddon/src/csplat.qc +++ b/quakec/csaddon/src/csplat.qc @@ -67,6 +67,7 @@ Available options: #define DP_EF_RED #define DP_ENT_CUSTOMCOLORMAP #define DP_ENT_EXTERIORMODELTOCLIENT +#define DP_ENT_SCALE #define DP_ENT_TRAILEFFECTNUM /* self.traileffectnum=particleeffectnum("myeffectname"); can be used to attach a particle trail to the given server entity. This is equivelent to calling trailparticles each frame. */ #define DP_ENT_VIEWMODEL #define DP_GECKO_SUPPORT @@ -148,6 +149,8 @@ Available options: #define DP_TE_CUSTOMFLASH #define DP_TE_EXPLOSIONRGB #define DP_TE_PARTICLECUBE +#define DP_TE_PARTICLERAIN +#define DP_TE_PARTICLESNOW #define DP_TE_SMALLFLASH #define DP_TE_SPARK #define DP_TE_STANDARDEFFECTBUILTINS @@ -160,10 +163,12 @@ Available options: #define FTE_CALLTIMEOFDAY /* Replication of mvdsv functionality (call calltimeofday to cause 'timeofday' to be called, with arguments that can be saved off to a global). Generally strftime is simpler to use. */ #define FTE_CSQC_ALTCONSOLES /* The engine tracks multiple consoles. These may or may not be directly visible to the user. */ #define FTE_CSQC_BASEFRAME /* Specifies that .basebone, .baseframe2, .baselerpfrac, baseframe1time, etc exist in csqc. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations. */ -#define FTE_QC_BASEFRAME /* Specifies that .basebone and .baseframe exist in ssqc. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations, from ssqc. */ #define FTE_CSQC_SERVERBROWSER #define FTE_CSQC_SKELETONOBJECTS /* Provides container objects for skeletal bone data, which can be modified on a per bone basis if needed. This allows you to dynamically generate animations (or just blend them with greater customisation) instead of being limited to a single animation or two. */ +#define FTE_CSQC_RAWIMAGES /* Provides raw rgba image access to csqc. With this, the csprogs can read textures into qc-accessible memory, modify it, and then upload it to the renderer. */ #define FTE_CSQC_RENDERTARGETS /* VF_RT_DESTCOLOUR exists and can be used to redirect any rendering to a texture instead of the screen. */ +#define FTE_CSQC_REVERB /* Specifies that the mod can create custom reverb effects. Whether they will actually be used or not depends upon the sound driver. */ +#define FTE_CSQC_WINDOWCAPTION /* Provides csqc with the ability to change the window caption as displayed when running windowed or in the task bar when switched out. */ #define FTE_ENT_SKIN_CONTENTS /* self.skin = CONTENTS_WATER; makes a brush entity into water. use -16 for a ladder. */ #define FTE_ENT_UNIQUESPAWNID #define FTE_EXTENDEDTEXTCODES @@ -183,9 +188,14 @@ Available options: #define FTE_PART_SCRIPT /* Specifies that the r_particledesc cvar can be used to select a list of particle effects to load from particles/*.cfg, the format of which is documented elsewhere. */ #define FTE_PART_NAMESPACES /* Specifies that the engine can use foo.bar to load effect foo from particle description bar. When used via ssqc, this should cause the client to download whatever effects as needed. */ #define FTE_PART_NAMESPACE_EFFECTINFO /* Specifies that effectinfo.bar can load effects from effectinfo.txt for DP compatibility. */ +#define FTE_QC_BASEFRAME /* Specifies that .basebone and .baseframe exist in ssqc. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations, from ssqc. */ +#define FTE_QC_FILE_BINARY /* Extends FRIK_FILE with binary read+write, as well as allowing seeking. Requires pointers. */ +#define FTE_QC_CHANGELEVEL_HUB /* Adds an extra argument to changelevel which is carried over to the next map in the 'spawnspot' global. Maps will be saved+reloaded until the extra argument is omitted again, purging all saved maps. Saved games will contain a copy of each preserved map. parm1-parm64 globals can be used, giving more space to transfer more player data. */ #define FTE_QC_CHECKCOMMAND /* Provides a way to test if a console command exists, and whether its a command/alias/cvar. Does not say anything about the expected meanings of any arguments or values. */ #define FTE_QC_CHECKPVS -#define FTE_QC_HARDWARECURSORS /* setcursormode exists in both csqc+menuqc, and accepts additional arguments to specify a cursor image to use when this module has focus. If the image exceeds hardware limits, it will be emulated using regular draws - this at least still avoids conflicting cursors. */ +#define FTE_QC_CROSSPRODUCT +#define FTE_QC_FS_SEARCH_SIZEMTIME +#define FTE_QC_HARDWARECURSORS /* setcursormode exists in both csqc+menuqc, and accepts additional arguments to specify a cursor image to use when this module has focus. If the image exceeds hardware limits (or hardware cursors are unsupported), it will be emulated using regular draws - this at least still avoids conflicting cursors as only one will ever be used, even if console+menu+csqc are all overlayed. */ #define FTE_QC_HASHTABLES /* Provides efficient string-based lookups. */ #define FTE_QC_INTCONV /* Provides string<>int conversions, including hex representations. */ #define FTE_QC_MATCHCLIENTNAME @@ -193,6 +203,7 @@ Available options: #define FTE_QC_PERSISTENTTEMPSTRINGS /* Supersedes DP_QC_MULTIPLETEMPSTRINGS. Temp strings are garbage collected automatically, and do not expire while they're still in use. This makes strzone redundant. */ #define FTE_QC_RAGDOLL_WIP #define FTE_QC_SENDPACKET /* Allows the use of out-of-band udp packets to/from other hosts. Includes the SV_ParseConnectionlessPacket event. */ +#define FTE_QC_STUFFCMDFLAGS /* Variation on regular stuffcmd that gives control over how spectators/mvds should be treated. */ #define FTE_QC_TRACETRIGGER #define FTE_QUAKE2_CLIENT /* This engine is able to act as a quake2 client */ #define FTE_QUAKE2_SERVER /* This engine is able to act as a quake2 server */ @@ -500,6 +511,13 @@ const int CONTENTBIT_SKY = 0x80000000i; const int CONTENTBITS_POINTSOLID = CONTENTBIT_SOLID|0x00000002|CONTENTBIT_BODY; /* Bits that traceline would normally consider solid */ const int CONTENTBITS_BOXSOLID = CONTENTBIT_SOLID|0x00000002|CONTENTBIT_BODY|CONTENTBIT_PLAYERCLIP; /* Bits that tracebox would normally consider solid */ const int CONTENTBITS_FLUID = CONTENTBIT_WATER|CONTENTBIT_SLIME|CONTENTBIT_LAVA|CONTENTBIT_SKY; +const int SPA_POSITION; /* These SPA_* constants are to specify which attribute is returned by the getsurfacepointattribute builtin */ +const int SPA_S_AXIS = 1; +const int SPA_T_AXIS = 2; +const int SPA_R_AXIS = 3; /* aka: SPA_NORMAL */ +const int SPA_TEXCOORDS0 = 4; +const int SPA_LIGHTMAP0_TEXCOORDS = 5; +const int SPA_LIGHTMAP0_COLOR = 6; #define CHAN_AUTO 0 /* The automatic channel, play as many sounds on this channel as you want, and they'll all play, however the other channels will replace each other. */ #define CHAN_WEAPON 1 #define CHAN_VOICE 2 @@ -737,10 +755,12 @@ Note that any rendertarget textures may be destroyed on video mode changes or so #define TEREDIT_MESH_KILL 16 #define TEREDIT_TINT 17 #define TEREDIT_RESET_SECT 20 -#define TEREDIT_RELOAD_SECT 21 +#define TEREDIT_RELOAD_SECT 21 #define TEREDIT_ENTS_WIPE 22 -#define TEREDIT_ENTS_CONCAT 23 -#define TEREDIT_ENTS_GET 24 +#define TEREDIT_ENT_GET 26 +#define TEREDIT_ENT_SET 27 +#define TEREDIT_ENT_ADD 28 +#define TEREDIT_ENT_COUNT 29 #define SLIST_HOSTCACHEVIEWCOUNT 0 #define SLIST_HOSTCACHETOTALCOUNT 1 #define SLIST_MASTERQUERYCOUNT 2 @@ -804,7 +824,7 @@ void(entity e) remove = #15; /* Destroys the given entity and clears some limited fields (including model, modelindex, solid, classname). Any references to the entity following the call are an error. After two seconds, the entity will be reused, in the interim you can unfortunatly still read its fields to see if the reference is no longer valid. */ void(vector v1, vector v2, float flags, entity ent) traceline = #16; /* - Traces an infinitely thin line through the world from v1 towards v2. + Traces a thin line through the world from v1 towards v2. Will not collide with ent, ent.owner, or any entity who's owner field refers to ent. The passed entity will also be used to determine whether to use a capsule trace, the contents that the trace should impact, and a couple of other extra fields that define the trace. There are no side effects beyond the trace_* globals being written. @@ -926,6 +946,9 @@ void (vector pos, string samp, float vol, float atten) ambientsound = #74; string(string str) precache_model2 = #75; string(string str) precache_sound2 = #76; string(string str) precache_file2 = #77; +string(entity e, string key) infokey = #80; /* Part of QW_ENGINE + If e is world, returns the field 'key' from either the serverinfo or the localinfo. If e is a player, returns the value of 'key' from the player's userinfo string. There are a few special exceptions, like 'ip' which is not technically part of the userinfo. */ + float(string) stof = #81; /* Part of FRIK_FILE, FTE_STRINGS, QW_ENGINE, ZQ_QC_STRINGS*/ void(vector start, vector mins, vector maxs, vector end, float nomonsters, entity ent) tracebox = #90; /* Part of DP_QC_TRACEBOX Exactly like traceline, but a box instead of a uselessly thin point. Acceptable sizes are limited by bsp format, q1bsp has strict acceptable size values. */ @@ -962,7 +985,7 @@ float(string extname) checkextension = #99; /* Use cvar("pr_checkextension") to see if this builtin exists. */ float(__variant funcref) checkbuiltin = #0:checkbuiltin; /* - Checks to see if the specified builtin is supported/mapped. This is intended as a way to check for #0 functions, allowing for simple single-builtin functions. */ + Checks to see if the specified builtin is supported/mapped. This is intended as a way to check for #0 functions, allowing for simple single-builtin functions. Warning, if two different engines map different builtins to the same number, then this function will not tell you which will be called, only that it won't crash (the exception being #0, which are remapped as available). */ float(float value) anglemod = #102; filestream(string filename, float mode, optional float mmapminsize) fopen = #110; /* Part of FRIK_FILE @@ -975,17 +998,17 @@ string(filestream fhandle) fgets = #112; /* Part of FRIK_FILE void(filestream fhandle, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7) fputs = #113; /* Part of FRIK_FILE Writes the given string(s) into the file. For compatibility with fgets, you should ensure that the string is terminated with a \n - this will not otherwise be done for you. It is up to the engine whether dos or unix line endings are actually written. */ -int(filestream fhandle, void *ptr, int size) fread = #0:fread; /* +int(filestream fhandle, void *ptr, int size) fread = #0:fread; /* Part of FTE_QC_FILE_BINARY Reads binary data out of the file. Returns truncated lengths if the read exceeds the length of the file. */ -int(filestream fhandle, void *ptr, int size) fwrite = #0:fwrite; /* +int(filestream fhandle, void *ptr, int size) fwrite = #0:fwrite; /* Part of FTE_QC_FILE_BINARY Writes binary data out of the file. */ #define ftell fseek //c compat -int(filestream fhandle, optional int newoffset) fseek = #0:fseek; /* +int(filestream fhandle, optional int newoffset) fseek = #0:fseek; /* Part of FTE_QC_FILE_BINARY Changes the current position of the file, if specified. Returns prior position, in bytes. */ -int(filestream fhandle, optional int newsize) fsize = #0:fsize; /* +int(filestream fhandle, optional int newsize) fsize = #0:fsize; /* Part of FTE_QC_FILE_BINARY Reports the total size of the file, in bytes. Can also be used to truncate/extend the file */ float(string s) strlen = #114; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/ @@ -1116,10 +1139,10 @@ int(string) stoh = #261; /* Part of FTE_QC_INTCONV string(int) htos = #262; /* Part of FTE_QC_INTCONV Formats an integer as a base16 string, with leading 0s and no prefix. Always returns 8 characters. */ -int(float) ftoi = #0:ftoi; /* +int(float) ftoi = #0:ftoi; /* Part of FTE_QC_INTCONV Converts the given float into a true integer without depending on extended qcvm instructions. */ -float(int) itof = #0:itof; /* +float(int) itof = #0:itof; /* Part of FTE_QC_INTCONV Converts the given true integer into a float without depending on extended qcvm instructions. */ float(float modlindex, optional float useabstransforms) skel_create = #263; /* Part of FTE_CSQC_SKELETONOBJECTS @@ -1171,10 +1194,10 @@ float(float modidx, float framenum) frameduration = #277; /* Part of FTE_CSQC_SK Retrieves the duration (in seconds) of the specified framegroup. */ #define dotproduct(v1,v2) ((vector)(v1)*(vector)(v2)) -vector(vector v1, vector v2) crossproduct = #0:crossproduct; /* +vector(vector v1, vector v2) crossproduct = #0:crossproduct; /* Part of FTE_QC_CROSSPRODUCT Small helper function to calculate the crossproduct of two vectors. */ -void(float action, optional vector pos, optional float radius, optional float quant, ...) terrain_edit = #278; /* +void(float action, optional vector pos, optional float radius, optional float quant, ...) terrain_edit = #278; /* Part of FTE_TERRAIN_MAP Realtime terrain editing. Actions are the TEREDIT_ constants. */ typedef struct @@ -1187,25 +1210,25 @@ typedef struct vector tdir; float tbias; } brushface_t; -int(float modelidx, int brushid, brushface_t *out_faces, int maxfaces, int *out_contents) brush_get = #0:brush_get; /* +int(float modelidx, int brushid, brushface_t *out_faces, int maxfaces, int *out_contents) brush_get = #0:brush_get; /* Part of FTE_RAW_MAP Queries a brush's information. You must pre-allocate the face array for the builtin to write to. Return value is the number of faces retrieved, 0 on error. */ -int(float modelidx, brushface_t *in_faces, int numfaces, int contents, optional int brushid) brush_create = #0:brush_create; /* +int(float modelidx, brushface_t *in_faces, int numfaces, int contents, optional int brushid) brush_create = #0:brush_create; /* Part of FTE_RAW_MAP Inserts a new brush into the model. Return value is the new brush's id. */ -void(float modelidx, int brushid) brush_delete = #0:brush_delete; /* +void(float modelidx, int brushid) brush_delete = #0:brush_delete; /* Part of FTE_RAW_MAP Destroys the specified brush. */ -float(float modelid, int brushid, int faceid, float selectedstate) brush_selected = #0:brush_selected; /* +float(float modelid, int brushid, int faceid, float selectedstate) brush_selected = #0:brush_selected; /* Part of FTE_RAW_MAP Allows you to easily set transient visual properties of a brush. returns old value. selectedstate=-1 changes nothing (called for its return value). */ -int(float modelid, int brushid, int faceid, vector *points, int maxpoints) brush_getfacepoints = #0:brush_getfacepoints; /* +int(float modelid, int brushid, int faceid, vector *points, int maxpoints) brush_getfacepoints = #0:brush_getfacepoints; /* Part of FTE_RAW_MAP Returns the list of verticies surrounding the given face. If face is 0, returns the center of the brush (if space for 1 point) or the mins+maxs (if space for 2 points). */ -int(int faceid, brushface_t *in_faces, int numfaces, vector *points, int maxpoints) brush_calcfacepoints = #0:brush_calcfacepoints; /* +int(int faceid, brushface_t *in_faces, int numfaces, vector *points, int maxpoints) brush_calcfacepoints = #0:brush_calcfacepoints; /* Part of FTE_RAW_MAP Determines the points of the specified face, if the specified brush were to actually be created. */ -int(float modelid, vector *planes, float *dists, int numplanes, int *out_brushes, int *out_faces, int maxresults) brush_findinvolume = #0:brush_findinvolume; /* +int(float modelid, vector *planes, float *dists, int numplanes, int *out_brushes, int *out_faces, int maxresults) brush_findinvolume = #0:brush_findinvolume; /* Part of FTE_RAW_MAP Allows you to easily obtain a list of brushes+faces within the given bounding region. If out_faces is not null, the same brush might be listed twice. */ void(optional entity ent, optional vector neworigin) touchtriggers = #279; /* @@ -1320,10 +1343,10 @@ float(string name) iscachedpic = #316; /* string(string name, optional float trywad) precache_pic = #317; /* Forces the engine to load the named image. If trywad is specified, the specified name must any lack path and extension. */ -void(string imagename, int width, int height, int *pixeldata) r_uploadimage = #0:r_uploadimage; /* +void(string imagename, int width, int height, int *pixeldata) r_uploadimage = #0:r_uploadimage; /* Part of FTE_CSQC_RAWIMAGES Updates a texture with the specified rgba data. Will be created if needed. */ -int*(string filename, __out int width, __out int height) r_readimage = #0:r_readimage; /* +int*(string filename, __out int width, __out int height) r_readimage = #0:r_readimage; /* Part of FTE_CSQC_RAWIMAGES Reads and decodes an image from disk, providing raw pixel data. Returns __NULL__ if the image could not be read for any reason. Use memfree to free the data once you're done with it. */ #define draw_getimagesize drawgetimagesize @@ -1480,7 +1503,7 @@ typedef struct { float flRoomRolloffFactor; int iDecayHFLimit; } reverbinfo_t; -void(float reverbslot, reverbinfo_t *reverbinfo, int sizeofreverinfo_t) setup_reverb = #0:setup_reverb; /* +void(float reverbslot, reverbinfo_t *reverbinfo, int sizeofreverinfo_t) setup_reverb = #0:setup_reverb; /* Part of FTE_CSQC_REVERB Reconfigures a reverb slot for weird effects. Slot 0 is reserved for no effects. Slot 1 is reserved for underwater effects. Reserved slots will be reinitialised on snd_restart, but can otherwise be changed. These reverb slots can be activated with SetListener. Note that reverb will currently only work when using OpenAL. */ void(string cmdname) registercommand = #352; /* @@ -1581,7 +1604,7 @@ void(string conname, vector pos, vector size, float fontsize) con_draw = #393; / float(string conname, float inevtype, float parama, float paramb, float paramc) con_input = #394; /* Part of FTE_CSQC_ALTCONSOLES Forwards input events to the named console. Mouse updates should be absolute only. */ -void(string newcaption) setwindowcaption = #0:setwindowcaption; /* +void(string newcaption) setwindowcaption = #0:setwindowcaption; /* Part of FTE_CSQC_WINDOWCAPTION Replaces the title of the game window, as seen when task switching or just running in windowed mode. */ float() cvars_haveunsaved = #0:cvars_haveunsaved; /* @@ -1602,8 +1625,8 @@ void(vector org, vector dir, float count) te_blood = #405; /* Part of DP_TE_BLOO void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower = #406; /* Part of _DP_TE_BLOODSHOWER*/ void(vector org, vector color) te_explosionrgb = #407; /* Part of DP_TE_EXPLOSIONRGB*/ void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube = #408; /* Part of DP_TE_PARTICLECUBE*/ -void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain = #409; /* Part of _DP_TE_PARTICLERAIN*/ -void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow = #410; /* Part of _DP_TE_PARTICLESNOW*/ +void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain = #409; /* Part of DP_TE_PARTICLERAIN*/ +void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow = #410; /* Part of DP_TE_PARTICLESNOW*/ void(vector org, vector vel, float howmany) te_spark = #411; /* Part of DP_TE_SPARK*/ void(vector org) te_gunshotquad = #412; /* Part of _DP_TE_QUADEFFECTS1*/ void(vector org) te_spikequad = #413; /* Part of _DP_TE_QUADEFFECTS1*/ @@ -1646,10 +1669,10 @@ float(searchhandle handle) search_getsize = #446; /* Part of DP_QC_FS_SEARCH string(searchhandle handle, float num) search_getfilename = #447; /* Part of DP_QC_FS_SEARCH Retrieves name of one of the files that was found by the initial search. */ -float(searchhandle handle, float num) search_getfilesize = #0:search_getfilesize; /* +float(searchhandle handle, float num) search_getfilesize = #0:search_getfilesize; /* Part of FTE_QC_FS_SEARCH_SIZEMTIME Retrieves the size of one of the files that was found by the initial search. */ -string(searchhandle handle, float num) search_getfilemtime = #0:search_getfilemtime; /* +string(searchhandle handle, float num) search_getfilemtime = #0:search_getfilemtime; /* Part of FTE_QC_FS_SEARCH_SIZEMTIME Retrieves modification time of one of the files. */ string(string cvarname) cvar_string = #448; /* Part of DP_QC_CVAR_STRING*/ diff --git a/quakec/csaddon/src/editor_ents.qc b/quakec/csaddon/src/editor_ents.qc index 8a0ee5dd..c573d468 100644 --- a/quakec/csaddon/src/editor_ents.qc +++ b/quakec/csaddon/src/editor_ents.qc @@ -151,14 +151,16 @@ entedit_t*() editor_ents_new = { local int nent; local entedit_t *newents; - nent = numents; - numents += 1i; - - //extend the list - newents = memalloc(sizeof(entedit_t)*numents); - memcpy((__variant*)newents, (__variant*)editents, sizeof(entedit_t)*nent); - memfree((__variant*)editents); - editents = newents; + nent = terrain_edit(TEREDIT_ENT_ADD, ""); + if (nent >= numents) + { + //extend the list + newents = memalloc(sizeof(entedit_t)*(nent+1)); + memcpy((__variant*)newents, (__variant*)editents, sizeof(entedit_t)*numents); + memfree((__variant*)editents); + editents = newents; + numents = nent+1; + } editents[nent].fields = hash_createtab(12, EV_STRING); @@ -170,11 +172,35 @@ void(float num) editor_ents_delete = if (num >= 0 && num < numents) { hash_destroytab(editents[num].fields); - numents--; - memcpy(&editents[num], &editents[num+1], sizeof(entedit_t) * (numents-num)); + editents[num].fields = 0; + terrain_edit(TEREDIT_ENT_SET, num, __NULL__); } }; +string(entedit_t *ent) reforment = +{ + string n = ""; + for (int i = 0; ; i++) + { + string key, value; + key = hash_getkey(ent->fields, i); + if isnull(key) + break; + if (key == "{") + continue; + value = ent->fields[key]; + //inject markup into the value so that it doesn't get too corrupted + value = strreplace("\\", "\\\\", value); + value = strreplace("\"", "\\\"", value); + value = strreplace("\n", "\\n", value); + //these are more optional + value = strreplace("\t", "\\t", value); + value = strreplace("\r", "\\r", value); + n = strcat(n, key, " \"", value, "\"\n"); + } + return n; +}; + void() updatemodelents = { entsdirty = FALSE; @@ -183,47 +209,18 @@ void() updatemodelents = for (int e = 0; e < numents; e++) { local entedit_t *ent = &editents[e]; - string n; - n = "{\n"; - for (int i = 0; ; i++) - { - string key, value; - key = hash_getkey(ent->fields, i); - if isnull(key) - break; - value = ent->fields[key]; - //inject markup into the value so that it doesn't get too corrupted - value = strreplace("\\", "\\\\", value); - value = strreplace("\"", "\\\"", value); - value = strreplace("\n", "\\n", value); - //these are more optional - value = strreplace("\t", "\\t", value); - value = strreplace("\r", "\\r", value); - n = strcat(n, key, " \"", value, "\"\n"); - } - n = strcat(n, "}\n"); - - terrain_edit(TEREDIT_ENTS_CONCAT, n); + string n = reforment(ent); + terrain_edit(TEREDIT_ENT_SET, e, n); } }; -float(float mode) editor_ents_poll = +void(entedit_t *ent) editor_ents_edited = { - if (mode != MODE_ENTSEDIT) - { - updatemodelents(); - localcmd("mod_terrain_save\n"); //saves a .ent if its a bsp, a .map if it has brushes, and a .hmp if otherwise. or something. - ca_checksave = __NULL__; - return TRUE; - } - return FALSE; -} -void() editor_ents_edited = -{ - ca_checksave = editor_ents_poll; - entsdirty = TRUE; entsapplytime = cltime+2; + + string n = reforment(ent); + terrain_edit(TEREDIT_ENT_SET, ent-editents, n); } inline float(string model) modelindexforname = @@ -265,7 +262,7 @@ void(entedit_t *nent) editor_ents_updated = { if (classn == entclasses[i].classn) { - nent->modelindex = modelindexforname(entclasses[i].model); + nent->modelindex = 0;//modelindexforname(entclasses[i].model); nent->alpha = 0.3; nent->colourmod = entclasses[i].colour; nent->mins = entclasses[i].mins; @@ -285,7 +282,6 @@ entedit_t*(entedit_t *o) editor_ents_clone = { int i; entedit_t *n = editor_ents_new(); - for (i = 0; ; i++) { @@ -301,52 +297,62 @@ entedit_t*(entedit_t *o) editor_ents_clone = return n; }; - void() editor_ents_reload = { + local entedit_t *nent; local string field, value; - //reset ent state - getentitytoken(__NULL__); + int id; + int f, fcount; - for(;;) + numents = terrain_edit(TEREDIT_ENT_COUNT); + editents = memalloc(sizeof(entedit_t)*numents); + for (id = 0; id < numents; id++) { - field = getentitytoken(); - if isnull(field) + field = terrain_edit(TEREDIT_ENT_GET, id); + nent = &editents[id]; + if (nent->fields) + hash_destroytab(nent->fields); + nent->fields = 0; + if (field == __NULL__) + continue; + nent->fields = hash_createtab(12, EV_STRING); + fcount = tokenize(field); + for (f = 0; f < fcount; f+=2) { - break; - } - if (field == "{") - { - local entedit_t *nent; - nent = editor_ents_new(); - - for(;;) - { - field = getentitytoken(); - if isnull(field) - { - print("Truncated ent lump\n"); - return; - } - if (field == "}") - break; - value = getentitytoken(); - - nent.fields[field] = value; -// print(sprintf("%s: %s\n", field, value)); - } - - editor_ents_updated(nent); - } - else - { - print(sprintf("Corrupt ent lump, found \"%s\"\n", field)); - return; + field = argv(f); + value = argv(f+1); + nent->fields[field] = value; } + editor_ents_updated(nent); } }; +//called when another client has edited an entity. +void(int idx, string new) CSQC_MapEntityEdited = +{ + if (idx >= numents) + { + if not (new) + return; //deleting an ent that doesn't already exist? :o + } + local entedit_t *ent = &editents[idx]; + + if (ent->fields) + hash_destroytab(ent->fields); + ent->fields = hash_createtab(12, EV_STRING); + int fcount = tokenize(new); + for (int f = 0; f < fcount; f+=2) + { + string field = argv(f); + string value = argv(f+1); + ent->fields[field] = value; + } + + editor_ents_updated(ent); +// editor_ents_edited(ent); +}; + void(string shadername, vector min, vector max, vector col) editor_ents_drawbbox = { if (min == max) @@ -520,7 +526,8 @@ float(float key, float unic, vector mousepos) editor_ents_key = { if (selectedent >= numents) return FALSE; - string value = editents[selectedent].fields[editkey]; + ent = &editents[selectedent]; + string value = ent->fields[editkey]; if (key == K_ESCAPE) { @@ -531,7 +538,7 @@ float(float key, float unic, vector mousepos) editor_ents_key = { if (!value) { - hash_delete(editents[selectedent].fields, editkey); + hash_delete(ent->fields, editkey); editfieldtype = 0; } else @@ -548,8 +555,9 @@ float(float key, float unic, vector mousepos) editor_ents_key = if (editfieldtype) { - editents[selectedent].fields[editkey] = value; - editor_ents_updated(&editents[selectedent]); + ent->fields[editkey] = value; + editor_ents_edited(ent); + editor_ents_updated(ent); } } else if (key == K_ESCAPE && selectedent) @@ -566,18 +574,18 @@ float(float key, float unic, vector mousepos) editor_ents_key = //figure out how far along the plane normal to push the entity in order to ensure that its mins/maxs is on the floor/slope/ceiling/wall/etc //yay dotproducts - float ext = [ + float ext = trace_plane_normal * [ (trace_plane_normal[0] < 0)?ent->mins[0]:ent->maxs[0], (trace_plane_normal[1] < 0)?ent->mins[1]:ent->maxs[1], (trace_plane_normal[2] < 0)?ent->mins[2]:ent->maxs[2] - ] * trace_plane_normal; + ]; //update the all important origin string str = sprintf("%v", trace_endpos + trace_plane_normal * ext); ent->fields["origin"] = str; //and fix up the quick-access stuff ent->org = stov(ent->fields["origin"]); - editor_ents_edited(); + editor_ents_edited(ent); } else return FALSE; @@ -612,7 +620,7 @@ void(vector mousepos) editor_ents_overlay = break; value = ent->fields[key]; col = '1 1 1'; - if (pickedit && mousepos_y >= pos_y && mousepos_y < pos_y + 8) + if (pickedit && mousepos_y >= pos_y && mousepos_y < pos_y + 8 && mousepos_x < 128) { col_y = 0; editkey = key; diff --git a/quakec/csaddon/src/opts.qc b/quakec/csaddon/src/opts.qc deleted file mode 100644 index c1f810af..00000000 --- a/quakec/csaddon/src/opts.qc +++ /dev/null @@ -1,3 +0,0 @@ -//this file must contain only definitions+pragmas. if it contains variables it will break the defs+crc. -//#pragma flag enable assumeint -//#pragma flag enable typeexplicit