From c4ded89ad7f0fda9d7042d89507383f3f225b8a7 Mon Sep 17 00:00:00 2001 From: Spoike Date: Fri, 13 Jan 2017 00:39:50 +0000 Subject: [PATCH] fix server crash (on two pk3s with the same pure path) fix fteqcc issue unintentionally stripping things added model event support. added support for a couple of extra twiddles in the iqm format. modelviewer now highlights meshes (to display hitmesh areas). modelviewer now shows ragdolls, because I can. fixed some nq download issues/crashes/fallbacks. fixed 8bit bmp palette issue. added capturethrottlesize cvar to throttle recording speed if disk space is getting low (to avoid Maverick's demo encoder getting swamped). com_protocolname can now list multiple protocol names to list multiple server types. only the first will be reported to other clients however. can now be compiled without support for q1bsp or q1mdl. not useful, but hey. increase max_channels, by as much as is necessary. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5038 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/client/cl_ents.c | 11 +- engine/client/cl_main.c | 22 +- engine/client/cl_parse.c | 45 +- engine/client/client.h | 1 + engine/client/console.c | 5 - engine/client/image.c | 31 +- engine/client/m_mp3.c | 244 ++++-- engine/client/m_options.c | 260 +++++- engine/client/merged.h | 4 + engine/client/net_master.c | 54 +- engine/client/pr_csqc.c | 26 +- engine/client/pr_menu.c | 4 +- engine/client/pr_skelobj.c | 352 +++++++- engine/client/r_2d.c | 1 + engine/client/r_part.c | 2 +- engine/client/r_surf.c | 105 ++- engine/client/renderer.c | 10 +- engine/client/snd_al.c | 14 +- engine/client/snd_dma.c | 24 +- engine/client/sound.h | 4 +- engine/client/sys_win.c | 8 +- engine/client/vid_headless.c | 108 +++ engine/common/bothdefs.h | 3 + engine/common/bspfile.h | 4 +- engine/common/com_mesh.c | 616 +++++++++++-- engine/common/com_mesh.h | 19 +- engine/common/com_phys_bullet.cpp | 24 +- engine/common/fs.c | 17 +- engine/common/gl_q2bsp.c | 2 +- engine/common/pr_bgcmd.c | 9 +- engine/common/pr_common.h | 11 +- engine/common/q1bsp.c | 1346 +++++++++++++++-------------- engine/dotnet2005/ftequake.sln | 59 +- engine/gl/gl_alias.c | 3 + engine/gl/gl_heightmap.c | 4 +- engine/gl/gl_hlmdl.c | 15 + engine/gl/gl_model.c | 88 +- engine/gl/gl_model.h | 6 +- engine/gl/gl_rlight.c | 10 +- engine/gl/model_hl.h | 13 +- engine/http/httpclient.c | 13 +- engine/qclib/pr_edict.c | 5 + engine/qclib/qcc.h | 11 + engine/qclib/qcc_pr_comp.c | 322 ++++--- engine/qclib/qcc_pr_lex.c | 9 + engine/qclib/qccmain.c | 22 +- engine/server/pr_cmds.c | 20 +- engine/server/progdefs.h | 3 + engine/server/sv_ents.c | 2 +- engine/server/sv_main.c | 4 +- engine/server/sv_move.c | 4 +- engine/server/sv_phys.c | 4 + engine/vk/vk_init.c | 71 +- 53 files changed, 2749 insertions(+), 1325 deletions(-) diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index d071daeb5..231110d2f 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -47,6 +47,8 @@ extern cvar_t gl_simpleitems; extern cvar_t cl_gibfilter, cl_deadbodyfilter; extern int cl_playerindex; +extern world_t csqc_world; + static struct predicted_player { int flags; @@ -3126,6 +3128,13 @@ static void CL_UpdateNetFrameLerpState(qboolean force, int curframe, int curbase } le->newframe[fst] = frame; le->newframestarttime[fst] = cl.servertime; + +// if (force) +// { +// //if its new, we need to tweak the age of the animation. looping anims won't appear any different, while non-looping ones will clamp to the last pose of the animation when its new. +// le->oldframestarttime[fst] -= Mod_GetFrameDuration(le->model, 0, le->oldframe[fst]); +// le->newframestarttime[fst] -= Mod_GetFrameDuration(le->model, 0, le->newframe[fst]); +// } } } } @@ -4075,7 +4084,7 @@ void CL_LinkPacketEntities (void) #ifdef RAGDOLL if (model && (model->dollinfo || le->skeletalobject)) - rag_updatedeltaent(ent, le); + rag_updatedeltaent(&csqc_world, ent, le); #endif ent->framestate.g[FS_REG].frame[0] &= ~0x8000; ent->framestate.g[FS_REG].frame[1] &= ~0x8000; diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 51dd90dcd..a2fbb8726 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -35,7 +35,7 @@ void QDECL Name_Callback(struct cvar_s *var, char *oldvalue); #define Name_Callback NULL #endif -void CL_ForceStopDownload (qdownload_t *dl, qboolean finish); +static void CL_ForceStopDownload (qboolean finish); // we need to declare some mouse variables here, because the menu system // references them even when on a unix system. @@ -160,7 +160,7 @@ cvar_t cl_sendguid = CVARD("cl_sendguid", "0", "Send a randomly generated 'gl cvar_t cl_downloads = CVARFD("cl_downloads", "1", CVAR_NOTFROMSERVER, "Allows you to block all automatic downloads."); cvar_t cl_download_csprogs = CVARFD("cl_download_csprogs", "1", CVAR_NOTFROMSERVER, "Download updated client gamecode if available. Warning: If you clear this to avoid downloading vm code, you should also clear cl_download_packages."); cvar_t cl_download_redirection = CVARFD("cl_download_redirection", "2", CVAR_NOTFROMSERVER, "Follow download redirection to download packages instead of individual files. Also allows the server to send nearly arbitary download commands.\n2: allows redirection only to named packages files (and demos/*.mvd), which is a bit safer."); -cvar_t cl_download_mapsrc = CVARD("cl_download_mapsrc", "", "Specifies an http location prefix for map downloads. EG: \"http://bigfoot.morphos-team.net/misc/quakemaps/\""); +cvar_t cl_download_mapsrc = CVARFD("cl_download_mapsrc", "", CVAR_ARCHIVE, "Specifies an http location prefix for map downloads. EG: \"http://bigfoot.morphos-team.net/misc/quakemaps/\""); cvar_t cl_download_packages = CVARFD("cl_download_packages", "1", CVAR_NOTFROMSERVER, "0=Do not download packages simply because the server is using them. 1=Download and load packages as needed (does not affect games which do not use this package). 2=Do download and install permanently (use with caution!)"); cvar_t requiredownloads = CVARFD("requiredownloads","1", CVAR_ARCHIVE, "0=join the game before downloads have even finished (might be laggy). 1=wait for all downloads to complete before joining."); @@ -3684,8 +3684,9 @@ void CL_DownloadSize_f(void) } void CL_FinishDownload(char *filename, char *tempname); -void CL_ForceStopDownload (qdownload_t *dl, qboolean finish) +static void CL_ForceStopDownload (qboolean finish) { + qdownload_t *dl = cls.download; if (Cmd_IsInsecure()) { Con_Printf(CON_WARNING "Execution from server rejected for %s\n", Cmd_Argv(0)); @@ -3713,15 +3714,13 @@ void CL_ForceStopDownload (qdownload_t *dl, qboolean finish) // get another file if needed CL_RequestNextDownload (); } - void CL_SkipDownload_f (void) { - CL_ForceStopDownload(cls.download, false); + CL_ForceStopDownload(false); } - void CL_FinishDownload_f (void) { - CL_ForceStopDownload(cls.download, true); + CL_ForceStopDownload(true); } #if defined(_WIN32) && !defined(WINRT) @@ -5795,13 +5794,15 @@ void Host_Shutdown(void) HTTP_CL_Terminate(); #endif + //disconnect server/client/etc + CL_Disconnect_f(); + + M_Shutdown(true); + #ifdef PLUGINS Plug_Shutdown(false); #endif - //disconnect server/client/etc - CL_Disconnect_f(); - #ifdef CSQC_DAT CSQC_Shutdown(); #endif @@ -5821,7 +5822,6 @@ void Host_Shutdown(void) #endif CL_FreeDlights(); CL_FreeVisEdicts(); - M_Shutdown(true); Mod_Shutdown(true); Wads_Flush(); Con_History_Save(); //do this outside of the console code so that the filesystem is still running at this point but still allowing the filesystem to make console prints (you might not see them, but they should be visible to sys_printf still, for debugging). diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index d7d4f12b0..7a7de9984 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -680,7 +680,11 @@ void CL_DisenqueDownload(char *filename) void CL_WebDownloadFinished(struct dl_download *dl) { if (dl->status == DL_FAILED) + { CL_DownloadFailed(dl->url, &dl->qdownload); + if (dl->qdownload.flags & DLLF_ALLOWWEB) //re-enqueue it if allowed, but this time not from the web server. + CL_EnqueDownload(dl->qdownload.localname, dl->qdownload.localname, dl->qdownload.flags & ~DLLF_ALLOWWEB); + } else if (dl->status == DL_FINISHED) { if (dl->file) @@ -697,9 +701,17 @@ void CL_SendDownloadStartRequest(char *filename, char *localname, unsigned int f qdownload_t *dl; #ifdef WEBCLIENT - if (!strncmp(filename, "http://", 7)) + if (!strncmp(filename, "http://", 7) || !strncmp(filename, "https://", 8)) { - if (!HTTP_CL_Get(filename, localname, CL_WebDownloadFinished)) + struct dl_download *wdl = HTTP_CL_Get(filename, localname, CL_WebDownloadFinished); + if (wdl) + { + if (!(flags & DLLF_TEMPORARY)) + Con_TPrintf ("Downloading %s to %s...\n", wdl->url, wdl->localname); + wdl->qdownload.flags = flags; + cls.download = &wdl->qdownload; + } + else CL_DownloadFailed(filename, NULL); return; } @@ -898,13 +910,22 @@ qboolean CL_CheckOrEnqueDownloadFile (const char *filename, const char *localnam SCR_EndLoadingPlaque(); //release console. - if (*cl_download_mapsrc.string) - if (!strncmp(filename, "maps/", 5)) - if (!strcmp(filename + strlen(filename)-4, ".bsp")) + if (flags & DLLF_ALLOWWEB) { - char base[MAX_QPATH]; - COM_FileBase(filename, base, sizeof(base)); - filename = va("%s%s.bsp", cl_download_mapsrc.string, base); + flags &= ~DLLF_ALLOWWEB; + if (*cl_download_mapsrc.string) + if (!strcmp(filename, localname)) + if (!strncmp(filename, "maps/", 5)) + if (!strcmp(filename + strlen(filename)-4, ".bsp")) + { + char base[MAX_QPATH]; + COM_FileBase(filename, base, sizeof(base)); + if (!strncmp(cl_download_mapsrc.string, "http://", 7) || !strncmp(cl_download_mapsrc.string, "https://", 8)) + filename = va("%s%s.bsp", cl_download_mapsrc.string, base); + else + filename = va("http://%s/%s.bsp", cl_download_mapsrc.string, base); + flags |= DLLF_ALLOWWEB; + } } if (!CL_EnqueDownload(filename, localname, flags)) @@ -2310,6 +2331,7 @@ void DL_Abort(qdownload_t *dl, enum qdlabort aborttype) if (dl->flags & DLLF_BEGUN) { + dl->flags &= ~DLLF_BEGUN; if (aborttype == QDL_COMPLETED) { //this file isn't needed now the download has finished. @@ -2414,7 +2436,8 @@ void DL_Abort(qdownload_t *dl, enum qdlabort aborttype) } dl->dlblocks = NULL; - Z_Free(dl); + if (dl->method != DL_HTTP) + Z_Free(dl); if (cls.download == dl) cls.download = NULL; } @@ -2654,6 +2677,8 @@ void CLDP_ParseDownloadBegin(char *s) return; } + if (dl->method == DL_QWPENDING) + dl->method = DL_DARKPLACES; if (dl->method != DL_DARKPLACES) { Con_Printf("Warning: download method isn't right.\n"); @@ -3571,7 +3596,7 @@ void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caution. } strcpy (cl.model_name[nummodels], str); if (*str != '*' && strcmp(str, "null")) //not inline models! - CL_CheckOrEnqueDownloadFile(str, NULL, ((nummodels==1)?DLLF_REQUIRED:0)); + CL_CheckOrEnqueDownloadFile(str, NULL, ((nummodels==1)?DLLF_REQUIRED|DLLF_ALLOWWEB:0)); Mod_TouchModel (str); } diff --git a/engine/client/client.h b/engine/client/client.h index ee6089946..e21ef8d53 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -550,6 +550,7 @@ typedef struct downloadlist_s { #define DLLF_USEREXPLICIT (1u<<7) //use explicitly requested it, ignore the cl_downloads cvar. #define DLLF_BEGUN (1u<<8) //server has confirmed that the file exists, is readable, and we've opened a file. should not be set on new requests. +#define DLLF_ALLOWWEB (1u<<9) //failed http downloads should retry but from the game server itself struct downloadlist_s *next; } downloadlist_t; diff --git a/engine/client/console.c b/engine/client/console.c index 3b957a877..db33cf051 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -1623,11 +1623,6 @@ void Con_PrintToSys(void) //returns the bottom of the progress bar static int Con_DrawProgress(int left, int right, int y) { -#ifdef RUNTIMELIGHTING - extern model_t *lightmodel; - extern long relitsurface; -#endif - conchar_t dlbar[1024], *chr; unsigned char progresspercenttext[128]; char *progresstext = NULL; diff --git a/engine/client/image.c b/engine/client/image.c index e15f2c711..9dff774c4 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -2108,7 +2108,7 @@ qbyte *ReadBMPFile(qbyte *buf, int length, int *width, int *height) for (i = 0; i < h.NumofColorIndices; i++) { - pal[i] = data[i*4+0] + (data[i*4+1]<<8) + (data[i*4+2]<<16) + (255/*data[i*4+3]*/<<16); + pal[i] = data[i*4+2] + (data[i*4+1]<<8) + (data[i*4+0]<<16) + (255/*data[i*4+3]*/<<24); } buf += h.OffsetofBMPBits; @@ -4436,6 +4436,7 @@ static void Image_LoadHiResTextureWorker(void *ctx, void *data, size_t a, size_t int firstex = (tex->flags & IF_EXACTEXTENSION)?tex_extensions_count-1:0; char *altname; char *nextalt; + qboolean exactext = !!(tex->flags & IF_EXACTEXTENSION); // Sys_Sleep(0.3); @@ -4453,18 +4454,26 @@ static void Image_LoadHiResTextureWorker(void *ctx, void *data, size_t a, size_t } //see if we recognise the extension, and only strip it if we do. - COM_FileExtension(altname, nicename, sizeof(nicename)); - e = 0; - if (strcmp(nicename, "lmp") && strcmp(nicename, "wal")) - for (; e < tex_extensions_count; e++) - { - if (!strcmp(nicename, (*tex_extensions[e].name=='.')?tex_extensions[e].name+1:tex_extensions[e].name)) - break; - } + if (exactext) + e = tex_extensions_count; + else + { + COM_FileExtension(altname, nicename, sizeof(nicename)); + e = 0; + if (strcmp(nicename, "lmp") && strcmp(nicename, "wal")) + for (; e < tex_extensions_count; e++) + { + if (!strcmp(nicename, (*tex_extensions[e].name=='.')?tex_extensions[e].name+1:tex_extensions[e].name)) + break; + } + } //strip it and try replacements if we do, otherwise assume that we're meant to be loading progs/foo.mdl_0.tga or whatever - if (e == tex_extensions_count || (tex->flags & IF_EXACTEXTENSION)) + if (e == tex_extensions_count || exactext) + { + exactext = true; Q_strncpyz(nicename, altname, sizeof(nicename)); + } else COM_StripExtension(altname, nicename, sizeof(nicename)); @@ -4511,7 +4520,7 @@ static void Image_LoadHiResTextureWorker(void *ctx, void *data, size_t a, size_t s = COM_SkipPath(nicename); n = basename; - while (*s && *s != '.' && n < basename+sizeof(basename)-5) + while (*s && (*s != '.'||exactext) && n < basename+sizeof(basename)-5) *n++ = *s++; s = strchr(s, '_'); if (s) diff --git a/engine/client/m_mp3.c b/engine/client/m_mp3.c index 2053fb681..dd031f7b5 100644 --- a/engine/client/m_mp3.c +++ b/engine/client/m_mp3.c @@ -16,6 +16,14 @@ #define WINAVI #endif +#if defined(__linux__) && !defined(ANDROID) + //should really include posix 2001 systems in general + #define HAVE_STATVFS +#endif +#ifdef HAVE_STATVFS + #include +#endif + typedef struct mediatrack_s{ char filename[MAX_QPATH]; @@ -2737,13 +2745,21 @@ int captureoldfbo; qboolean capturingfbo; texid_t capturetexture; qboolean captureframeforce; + #if defined(GLQUAKE) && !defined(GLESONLY) -//ring buffer -int pbo_handles[4]; -enum uploadfmt pbo_format; -#define CAN_USE_PBOS +#define GLQUAKE_PBOS #endif -int pbo_oldest; +struct +{ +#ifdef GLQUAKE_PBOS + int pbo_handle; +#endif + enum uploadfmt format; + int width; + int height; +} offscreen_queue[4]; //ringbuffer of offscreen_captureframe...captureframe +int offscreen_captureframe; +enum uploadfmt offscreen_format; #define CAPTURECODECDESC_DEFAULT "tga" #ifdef WINAVI @@ -2765,6 +2781,7 @@ cvar_t capturesound = CVARD("capturesound", "1", "Enables the capturing of game cvar_t capturesoundchannels = CVAR("capturesoundchannels", "2"); cvar_t capturesoundbits = CVAR("capturesoundbits", "16"); cvar_t capturemessage = CVAR("capturemessage", ""); +cvar_t capturethrottlesize = CVARD("capturethrottlesize", "0", "If set, capturing will slow down significantly if there is less disk space available than this cvar (in mb). This is useful when recording a stream to (ram)disk while another program is simultaneously consuming frames."); qboolean recordingdemo; media_encoder_funcs_t *currentcapture_funcs; @@ -2834,6 +2851,34 @@ static void QDECL capture_raw_video (void *vctx, void *data, int frame, int widt ctx->frames = frame+1; Q_snprintfz(filename, sizeof(filename), "%s%8.8i.%s", ctx->videonameprefix, frame, ctx->videonameextension); SCR_ScreenShot(filename, ctx->fsroot, &data, 1, width, height, fmt); + + if (capturethrottlesize.ival) + { + char base[MAX_QPATH]; + Q_strncpyz(base, ctx->videonameprefix, sizeof(base)); + *COM_SkipPath(base) = 0; + if (FS_NativePath(base, FS_GAMEONLY, filename, sizeof(filename))) + { + #if HAVE_STATVFS + //posix 2001 + struct statvfs inf; + if(0==statvfs(filename, &inf)) + { + if (inf.f_frsize*(double)inf.f_blocks < (1024.*1024)*capturethrottlesize.value) + Sys_Sleep(1); + } + #elif defined( _WIN32) + wchar_t ffs[MAX_OSPATH]; + ULARGE_INTEGER freebytes; + if (GetDiskFreeSpaceExW(widen(ffs, sizeof(ffs), filename), &freebytes, NULL, NULL)) + if (freebytes.QuadPart < (ULONGLONG)(1024*1024)*capturethrottlesize.value) + Sys_Sleep(1); + #else + Con_Printf("capturethrottlesize is unsupported in this build\n"); + capturethrottlesize.ival = 0; + #endif + } + } } static void QDECL capture_raw_audio (void *vctx, void *data, int bytes) { @@ -2882,10 +2927,10 @@ static void QDECL capture_avi_end(void *vctx) { struct capture_avi_ctx *ctx = vctx; - if (ctx->uncompressed_video_stream) qAVIStreamRelease(ctx->uncompressed_video_stream); - if (ctx->compressed_video_stream) qAVIStreamRelease(ctx->compressed_video_stream); - if (ctx->uncompressed_audio_stream) qAVIStreamRelease(ctx->uncompressed_audio_stream); - if (ctx->file) qAVIFileRelease(ctx->file); + if (ctx->uncompressed_video_stream) qAVIStreamRelease(ctx->uncompressed_video_stream); + if (ctx->compressed_video_stream) qAVIStreamRelease(ctx->compressed_video_stream); + if (ctx->uncompressed_audio_stream) qAVIStreamRelease(ctx->uncompressed_audio_stream); + if (ctx->file) qAVIFileRelease(ctx->file); Z_Free(ctx); } @@ -3230,7 +3275,7 @@ void Media_RecordFrame (void) y = vid.height-8; #ifdef GLQUAKE - if (capturingfbo) + if (capturingfbo && qrenderer == QR_OPENGL) { shader_t *pic; GLBE_FBO_Pop(captureoldfbo); @@ -3301,56 +3346,69 @@ void Media_RecordFrame (void) if (R2D_Flush) R2D_Flush(); -#ifdef CAN_USE_PBOS - if (pbo_format != TF_INVALID) +#ifdef GLQUAKE_PBOS + if (offscreen_format != TF_INVALID && qrenderer == QR_OPENGL) { - int imagesize = vid.fbpwidth * vid.fbpheight; - switch(pbo_format) + int frame; + //encode the frame if we're about to stomp on it + while (offscreen_captureframe + countof(offscreen_queue) <= captureframe) { - case TF_BGR24: - case TF_RGB24: - imagesize *= 3; - break; - case TF_BGRA32: - case TF_RGBA32: - imagesize *= 4; - break; - default: - break; - } - while (pbo_oldest + countof(pbo_handles) <= captureframe) - { - qglBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pbo_handles[pbo_oldest%countof(pbo_handles)]); + frame = offscreen_captureframe%countof(offscreen_queue); + qglBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, offscreen_queue[frame].pbo_handle); buffer = qglMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB); if (buffer) { //FIXME: thread these (with audio too, to avoid races) - currentcapture_funcs->capture_video(currentcapture_ctx, buffer, pbo_oldest, vid.fbpwidth, vid.fbpheight, pbo_format); + currentcapture_funcs->capture_video(currentcapture_ctx, buffer, offscreen_captureframe, offscreen_queue[frame].width, offscreen_queue[frame].height, offscreen_queue[frame].format); qglUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB); } - pbo_oldest++; + offscreen_captureframe++; } - if (!pbo_handles[captureframe%countof(pbo_handles)]) + frame = captureframe%countof(offscreen_queue); + //if we have no pbo yet, create one. + if (!offscreen_queue[frame].pbo_handle) { - qglGenBuffersARB(1, &pbo_handles[captureframe%countof(pbo_handles)]); - qglBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pbo_handles[captureframe%countof(pbo_handles)]); + int imagesize = 0; + offscreen_queue[frame].format = offscreen_format; + offscreen_queue[frame].width = vid.pixelwidth; + offscreen_queue[frame].height = vid.pixelheight; + switch(offscreen_queue[frame].format) + { + case TF_BGR24: + case TF_RGB24: + imagesize = 3; + break; + case TF_BGRA32: + case TF_RGBA32: + imagesize = 4; + break; + default: + break; + } + + imagesize *= offscreen_queue[frame].width * offscreen_queue[frame].height; + + qglGenBuffersARB(1, &offscreen_queue[frame].pbo_handle); + qglBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, offscreen_queue[frame].pbo_handle); qglBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, imagesize, NULL, GL_STATIC_READ_ARB); } - qglBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pbo_handles[captureframe%countof(pbo_handles)]); - switch(pbo_format) + + //get the gpu to copy the texture into the pbo. the driver should pipeline this read until we actually map the pbo, hopefully avoiding stalls + qglBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, offscreen_queue[frame].pbo_handle); + switch(offscreen_queue[frame].format) { case TF_BGR24: - qglReadPixels(0, 0, vid.fbpwidth, vid.fbpheight, GL_BGR_EXT, GL_UNSIGNED_BYTE, 0); + qglReadPixels(0, 0, offscreen_queue[frame].width, offscreen_queue[frame].height, GL_BGR_EXT, GL_UNSIGNED_BYTE, 0); break; case TF_BGRA32: - qglReadPixels(0, 0, vid.fbpwidth, vid.fbpheight, GL_BGRA_EXT, GL_UNSIGNED_INT_8_8_8_8_REV, 0); + qglReadPixels(0, 0, offscreen_queue[frame].width, offscreen_queue[frame].height, GL_BGRA_EXT, GL_UNSIGNED_INT_8_8_8_8_REV, 0); break; case TF_RGB24: - qglReadPixels(0, 0, vid.fbpwidth, vid.fbpheight, GL_RGB, GL_UNSIGNED_BYTE, 0); + qglReadPixels(0, 0, offscreen_queue[frame].width, offscreen_queue[frame].height, GL_RGB, GL_UNSIGNED_BYTE, 0); break; case TF_RGBA32: - qglReadPixels(0, 0, vid.fbpwidth, vid.fbpheight, GL_RGBA, GL_UNSIGNED_BYTE, 0); + qglReadPixels(0, 0, offscreen_queue[frame].width, offscreen_queue[frame].height, GL_RGBA, GL_UNSIGNED_BYTE, 0); break; default: break; @@ -3359,8 +3417,38 @@ void Media_RecordFrame (void) } else #endif +#if 0//def VKQUAKE + if (offscreen_format != TF_INVALID && qrenderer == QR_VULKAN) { - pbo_oldest = captureframe+1; + //try and collect any finished frames + while (offscreen_captureframe + countof(offscreen_queue) <= captureframe) + { + frame = offscreen_captureframe%countof(offscreen_queue); + vkFenceWait(); + buffer = NULL;//qglMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB); + if (buffer) + { + //FIXME: thread these (with audio too, to avoid races) + currentcapture_funcs->capture_video(currentcapture_ctx, buffer, offscreen_captureframe, offscreen_queue[frame].width, offscreen_queue[frame].height, offscreen_queue[frame].format); + //qglUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB); + } + offscreen_captureframe++; + } + + frame = captureframe%countof(offscreen_queue); + if (no frame yet) + { + create a buffer + map the buffer persistently + } + + vkCopyImageToBuffer + vkSubmitFence + } + else +#endif + { + offscreen_captureframe = captureframe+1; //submit the current video frame. audio will be mixed to match. buffer = VID_GetRGBInfo(&truewidth, &trueheight, &fmt); if (buffer) @@ -3388,7 +3476,7 @@ skipframe: y = vid.height-8; #ifdef GLQUAKE - if (capturingfbo) + if (capturingfbo && qrenderer == QR_OPENGL) { shader_t *pic; GLBE_FBO_Pop(captureoldfbo); @@ -3553,29 +3641,55 @@ void Media_InitFakeSoundDevice (int speed, int channels, int samplebits) void Media_StopRecordFilm_f (void) { -#ifdef CAN_USE_PBOS - if (pbo_format) +#ifdef GLQUAKE_PBOS + if (offscreen_format && qrenderer == QR_OPENGL) { - int i; - while (pbo_oldest < captureframe) + int frame; + qbyte *buffer; + while (offscreen_captureframe < captureframe) { - qbyte *buffer; - qglBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pbo_handles[pbo_oldest%countof(pbo_handles)]); + frame = offscreen_captureframe%countof(offscreen_queue); + qglBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, offscreen_queue[frame].pbo_handle); buffer = qglMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB); if (buffer) { - currentcapture_funcs->capture_video(currentcapture_ctx, buffer, pbo_oldest, vid.fbpwidth, vid.fbpheight, TF_BGR24); -// currentcapture_funcs->capture_video(currentcapture_ctx, buffer, pbo_oldest, vid.fbpwidth, vid.fbpheight, TF_BGRA32); + currentcapture_funcs->capture_video(currentcapture_ctx, buffer, offscreen_captureframe, offscreen_queue[frame].width, offscreen_queue[frame].height, offscreen_queue[frame].format); qglUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB); } - pbo_oldest++; + offscreen_captureframe++; } - for (i = 0; i < countof(pbo_handles); i++) + for (frame = 0; frame < countof(offscreen_queue); frame++) { - if (pbo_handles[i]) - qglDeleteBuffersARB(1, &pbo_handles[i]); - pbo_handles[i] = 0; + if (offscreen_queue[frame].pbo_handle) + qglDeleteBuffersARB(1, &offscreen_queue[frame].pbo_handle); + memset(&offscreen_queue[frame], 0, sizeof(offscreen_queue[frame])); + } + } +#endif +#if 0//def VKQUAKE + if (pbo_format && qrenderer == QR_VULKAN) + { + int frame; + while (offscreen_captureframe < captureframe) + { + frame = offscreen_captureframe%countof(offscreen_queue); + qbyte *buffer; + qglBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, offscreen_queue[frame].pbo_handle); + buffer = qglMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB); + if (buffer) + { + currentcapture_funcs->capture_video(currentcapture_ctx, buffer, offscreen_captureframe, offscreen_queue[frame].width, offscreen_queue[frame].height, offscreen_queue[frame].format); + qglUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB); + } + offscreen_captureframe++; + } + + for (frame = 0; frame < countof(offscreen_queue); frame++) + { + if (offscreen_queue[frame].pbo_handle) + qglDeleteBuffersARB(1, &offscreen_queue[frame].pbo_handle); + memset(&offscreen_queue[frame], 0, sizeof(offscreen_queue[frame])); } } #endif @@ -3596,7 +3710,7 @@ void Media_StopRecordFilm_f (void) currentcapture_funcs = NULL; #ifdef GLQUAKE - if (capturingfbo) + if (capturingfbo && qrenderer == QR_OPENGL) { GLBE_FBO_Pop(captureoldfbo); GLBE_FBO_Destroy(&capturefbo); @@ -3627,7 +3741,7 @@ static void Media_RecordFilm (char *recordingname, qboolean demo) Con_ClearNotify(); - captureframe = pbo_oldest = 0; + captureframe = offscreen_captureframe = 0; for (i = 0; i < sizeof(pluginencodersfunc)/sizeof(pluginencodersfunc[0]); i++) { if (pluginencodersfunc[i]) @@ -3668,17 +3782,26 @@ static void Media_RecordFilm (char *recordingname, qboolean demo) vid.fbpheight = captureheight.ival; vid.framebuffer = capturetexture; } + else #endif + { + vid.fbpwidth = vid.pixelwidth; + vid.fbpheight = vid.pixelheight; + } -#ifdef CAN_USE_PBOS - pbo_format = TF_INVALID; + offscreen_format = TF_INVALID; +#ifdef GLQUAKE_PBOS if (qrenderer == QR_OPENGL && !gl_config.gles && gl_config.glversion >= 2.1) { //both tgas and vfw favour bgr24, so lets get the gl drivers to suffer instead of us, where possible. if (vid.fbpwidth & 3) - pbo_format = TF_BGRA32; //don't bother changing pack alignment, just use something that is guarenteed to not need anything. + offscreen_format = TF_BGRA32; //don't bother changing pack alignment, just use something that is guarenteed to not need anything. else - pbo_format = TF_BGR24; + offscreen_format = TF_BGR24; } +#endif +#ifdef VKQUAKE +// if (qrenderer == QR_VULKAN) +// offscreen_format = TF_BGRA32; //use the native format, the driver won't do byteswapping for us. #endif recordingdemo = demo; @@ -4531,6 +4654,7 @@ void Media_Init(void) Cvar_Register(&captureheight, "AVI capture controls"); Cvar_Register(&capturedriver, "AVI capture controls"); Cvar_Register(&capturecodec, "AVI capture controls"); + Cvar_Register(&capturethrottlesize, "AVI capture controls"); #if defined(WINAVI) Cvar_Register(&capturesoundbits, "AVI capture controls"); diff --git a/engine/client/m_options.c b/engine/client/m_options.c index 7758016f4..cb9db792b 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -2753,6 +2753,11 @@ void M_Menu_Video_f (void) } #ifndef MINIMAL + +#ifdef RAGDOLL +#include "pr_common.h" +#endif + typedef struct { enum { @@ -2760,7 +2765,8 @@ typedef struct MV_BONES, MV_SHADER, MV_TEXTURE, - MV_COLLISION + MV_COLLISION, + MV_EVENTS, } mode; int surfaceidx; int skingroup; @@ -2776,6 +2782,17 @@ typedef struct char shaderfile[MAX_QPATH]; char *shadertext; + +#ifdef RAGDOLL + lerpents_t ragent; + world_t ragworld; + wedict_t ragworldedict; + comentvars_t ragworldvars; + comextentvars_t ragworldextvars; + pubprogfuncs_t ragfuncs; + qboolean flop; //ragdoll flopping enabled. + float fixedrate; +#endif } modelview_t; static unsigned int genhsv(float h_, float s, float v) @@ -2823,9 +2840,9 @@ static void M_BoneDisplayLame(entity_t *e, int *y, int depth, int parent, int fi if (Mod_GetTag(e->model, i+1, &e->framestate, result)) { #if 0//def _DEBUG - Draw_FunString(depth*16, *y, va("%s%i: %s (%g %g %g)", (i==sel)?"^1":"", i, bname, result[3], result[7], result[11])); + Draw_FunString(depth*16, *y, va("%s%i: %s (%g %g %g)", (i==sel)?"^1":"", i+1, bname, result[3], result[7], result[11])); #else - Draw_FunString(depth*16, *y, va("%s%i: %s", (i==sel)?"^1":"", i, bname)); + Draw_FunString(depth*16, *y, va("%s%i: %s", (i==sel)?"^1":"", i+1, bname)); #endif } else @@ -2836,6 +2853,17 @@ static void M_BoneDisplayLame(entity_t *e, int *y, int depth, int parent, int fi } } #endif + +static unsigned int tobit(unsigned int bitmask) +{ + unsigned int b; + for (b = 0; b < 32; b++) + { + if (bitmask & (1<flop) + ent.framestate.g[FS_REG].frame[0] |= 0x8000; + if (ent.model->dollinfo) + { + float rate = 1.0/60; + rag_doallanimations(&mods->ragworld); + mods->fixedrate += host_frametime; + if (mods->fixedrate > 1) + mods->fixedrate = 1; + while (mods->fixedrate >= rate) + { + sv.world.rbe->Frame(&mods->ragworld, rate, 800); + mods->fixedrate -= rate; + } + + rag_updatedeltaent(&mods->ragworld, &ent, &mods->ragent); + } #endif if (mods->mode == MV_COLLISION) { - shader_t *s = R_RegisterShader("bboxshader_nodepth", SUF_NONE, + shader_t *s; + +#ifdef HALFLIFEMODELS + if (ent.model->type == mod_halflife) + HLMDL_DrawHitBoxes(&ent); + else +#endif + if (1) + { + s = R_RegisterShader("hitbox_nodepth", SUF_NONE, "{\n" "polygonoffset\n" "{\n" @@ -2955,16 +3007,25 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_ "nodepthtest\n" "}\n" "}\n"); - -#ifdef HALFLIFEMODELS - if (ent.model->type == mod_halflife) - HLMDL_DrawHitBoxes(&ent); + Mod_AddSingleSurface(&ent, mods->surfaceidx, s); + } else -#endif { vec3_t mins, maxs; VectorAdd(ent.model->mins, ent.origin, mins); VectorAdd(ent.model->maxs, ent.origin, maxs); + + s = R_RegisterShader("bboxshader_nodepth", SUF_NONE, + "{\n" + "polygonoffset\n" + "{\n" + "map $whiteimage\n" + "blendfunc gl_src_alpha gl_one\n" + "rgbgen vertex\n" + "alphagen vertex\n" + "nodepthtest\n" + "}\n" + "}\n"); CLQ1_AddOrientedCube(s, mins, maxs, NULL, 1, 1, 1, 0.2); } @@ -2986,7 +3047,7 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_ "}\n" "}\n"); - if (ent.model->funcs.NativeTrace(ent.model, 0, &ent.framestate, NULL, v1, v2, vec3_origin, vec3_origin, false, ~0, &tr)) + if (ent.model->funcs.NativeTrace && ent.model->funcs.NativeTrace(ent.model, 0, &ent.framestate, NULL, v1, v2, vec3_origin, vec3_origin, false, ~0, &tr)) { vec3_t dir; float f; @@ -3067,13 +3128,26 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_ R_RenderView(); y = 0; - - fname = Mod_SurfaceNameForNum(ent.model, mods->surfaceidx); - if (!fname) - fname = "Unknown Surface"; - Draw_FunString(0, y, va("Surf %i: %s", mods->surfaceidx, fname)); - y+=8; - + { + fname = Mod_SurfaceNameForNum(ent.model, mods->surfaceidx); + if (!fname) + fname = "Unknown Surface"; + Draw_FunString(0, y, va("Surf %i: %s", mods->surfaceidx, fname)); + y+=8; + } + { + fname = Mod_SkinNameForNum(ent.model, mods->surfaceidx, mods->skingroup); + if (!fname) + { + Draw_FunString(0, y, va("Skin %i: ", mods->skingroup)); + } + else + { + shader = Mod_ShaderForSkin(ent.model, mods->surfaceidx, mods->skingroup); + Draw_FunString(0, y, va("Skin %i: \"%s\", shader \"%s\"", mods->skingroup, fname, shader?shader->name:"NO SHADER")); + } + y+=8; + } { char *fname; int numframes = 0; @@ -3084,12 +3158,6 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_ Draw_FunString(0, y, va("Frame%i: %s (%i poses, %f/%f secs, %s)", mods->framegroup, fname, numframes, ent.framestate.g[FS_REG].frametime[0], duration, loop?"looped":"unlooped")); y+=8; } - fname = Mod_SkinNameForNum(ent.model, mods->surfaceidx, mods->skingroup); - if (!fname) - fname = "Unknown Skin"; - shader = Mod_ShaderForSkin(ent.model, mods->surfaceidx, mods->skingroup); - Draw_FunString(0, y, va("Skin %i: (%s) %s", mods->skingroup, fname, shader?shader->name:"NO SHADER")); - y+=8; switch(mods->mode) { @@ -3100,22 +3168,75 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_ "s: zoom out\n" "m: mode\n" "r: reset times\n" - "home: skin-=1\n" - "end: skin+=1\n" + "home: skin+=1\n" + "end: skin-=1\n" "pgup: frame+=1\n" "pgdn: frame-=1\n" "mins: %g %g %g, maxs: %g %g %g\n", ent.model->mins[0], ent.model->mins[1], ent.model->mins[2], ent.model->maxs[0], ent.model->maxs[1], ent.model->maxs[2]) , CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_default, fs); break; case MV_COLLISION: - if (ent.model->type != mod_halflife) + if (!ent.model) + ; + else if (ent.model->type == mod_alias) + { + galiasinfo_t *inf = Mod_Extradata(ent.model); + int surfnum = mods->surfaceidx; + while(inf && surfnum-->0) + inf = inf->nextsurf; + if (inf) + { + char contents[512]; + unsigned int i; + char *contentnames[32] = {NULL}; + contentnames[tobit(FTECONTENTS_SOLID)] = "solid"; + contentnames[tobit(FTECONTENTS_LAVA)] = "lava"; + contentnames[tobit(FTECONTENTS_SLIME)] = "slime"; + contentnames[tobit(FTECONTENTS_WATER)] = "water"; + contentnames[tobit(FTECONTENTS_LADDER)] = "ladder"; + contentnames[tobit(FTECONTENTS_PLAYERCLIP)] = "playerclip"; + contentnames[tobit(FTECONTENTS_MONSTERCLIP)] = "monsterclip"; + contentnames[tobit(FTECONTENTS_BODY)] = "body"; + contentnames[tobit(FTECONTENTS_CORPSE)] = "corpse"; + contentnames[tobit(FTECONTENTS_SKY)] = "sky"; + for (*contents = 0, i = 0; i < 32; i++) + { + if (inf->contents & (1<mins[0], ent.model->mins[1], ent.model->mins[2], ent.model->maxs[0], ent.model->maxs[1], ent.model->maxs[2], + contents, + inf->csurface.flags, + inf->surfaceid, + inf->geomset>=MAX_GEOMSETS?-1:inf->geomset, inf->geomid, inf->geomset>=MAX_GEOMSETS?" (always)":"", + inf->numverts, inf->numindexes/3 + ) + , CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_default, fs); + } + } + else { R_DrawTextField(r_refdef.grect.x, r_refdef.grect.y+y, r_refdef.grect.width, r_refdef.grect.height-y, va("mins: %g %g %g, maxs: %g %g %g\n", ent.model->mins[0], ent.model->mins[1], ent.model->mins[2], ent.model->maxs[0], ent.model->maxs[1], ent.model->maxs[2]) , CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_default, fs); - break; } - //fallthrough + break; case MV_BONES: #ifdef SKELETALMODELS { @@ -3131,6 +3252,21 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_ } #endif break; + case MV_EVENTS: + { + int i; + float timestamp = 0; + int code = 0; + char *data = NULL; + Draw_FunString(0, y, va("Events: ")); + y+=8; + for (i = 0; Mod_GetModelEvent(ent.model, mods->framegroup, i, ×tamp, &code, &data); y+=8, i++) + { + Draw_FunString(0, y, va("%i %f: %i %s", i, timestamp, code, data)); + } + Draw_FunString(0, y, va("%f: ", Mod_GetFrameDuration(ent.model, 0, mods->framegroup))); + } + break; case MV_SHADER: { if (!mods->shadertext) @@ -3184,7 +3320,8 @@ static qboolean M_ModelViewerKey(struct menucustom_s *c, struct menu_s *m, int k case MV_BONES: mods->mode = MV_SHADER; break; case MV_SHADER: mods->mode = MV_TEXTURE; break; case MV_TEXTURE: mods->mode = MV_COLLISION; break; - case MV_COLLISION: mods->mode = MV_NONE; break; + case MV_COLLISION: mods->mode = MV_EVENTS; break; + case MV_EVENTS: mods->mode = MV_NONE; break; } } else if (key == 'r') @@ -3192,6 +3329,14 @@ static qboolean M_ModelViewerKey(struct menucustom_s *c, struct menu_s *m, int k mods->framechangetime = realtime; mods->skinchangetime = realtime; } +#ifdef RAGDOLL + else if (key == 'f') + { + mods->flop ^= 1; + if (!mods->flop) + rag_removedeltaent(&mods->ragent); + } +#endif else if (key == '[') mods->boneidx--; else if (key == ']') @@ -3204,14 +3349,14 @@ static qboolean M_ModelViewerKey(struct menucustom_s *c, struct menu_s *m, int k mods->yaw -= 5; else if (key == K_RIGHTARROW) mods->yaw += 5; - else if (key == K_HOME) + else if (key == K_END) { mods->skingroup = max(0, mods->skingroup-1); mods->skinchangetime = realtime; Z_Free(mods->shadertext); mods->shadertext = NULL; } - else if (key == K_END) + else if (key == K_HOME) { mods->skingroup += 1; mods->skinchangetime = realtime; @@ -3230,13 +3375,13 @@ static qboolean M_ModelViewerKey(struct menucustom_s *c, struct menu_s *m, int k mods->framegroup += 1; mods->framechangetime = realtime; } - else if (key == K_INS) + else if (key == K_DEL) { mods->surfaceidx = max(0, mods->surfaceidx-1); Z_Free(mods->shadertext); mods->shadertext = NULL; } - else if (key == K_DEL) + else if (key == K_INS) { mods->surfaceidx += 1; Z_Free(mods->shadertext); @@ -3253,12 +3398,31 @@ static qboolean M_ModelViewerKey(struct menucustom_s *c, struct menu_s *m, int k return true; } +#ifdef RAGDOLL +void M_Modelviewer_Shutdown(struct menu_s *menu) +{ + modelview_t *mv = menu->data; + rag_removedeltaent(&mv->ragent); + skel_reset(&mv->ragworld); + World_RBE_Shutdown(&mv->ragworld); +} +//haxors, for skeletal objects+RBE +char *PDECL M_Modelviewer_AddString(pubprogfuncs_t *prinst, const char *val, int minlength, pbool demarkup) +{ + return Z_Malloc(minlength); +} +struct edict_s *PDECL M_Modelviewer_ProgsToEdict(pubprogfuncs_t *prinst, int num) +{ + return (struct edict_s*)prinst->edicttable[num]; +} +#endif + void M_Menu_ModelViewer_f(void) { modelview_t *mv; menucustom_t *c; menu_t *menu; - + Key_Dest_Add(kdm_emenu); menu = M_CreateMenu(sizeof(*mv)); @@ -3268,10 +3432,28 @@ void M_Menu_ModelViewer_f(void) c->draw = M_ModelViewerDraw; c->key = M_ModelViewerKey; - mv->yaw = 180 + crandom()*45; + mv->yaw = 180;// + crandom()*45; mv->dist = 150; Q_strncpyz(mv->modelname, Cmd_Argv(1), sizeof(mv->modelname)); Q_strncpyz(mv->forceshader, Cmd_Argv(2), sizeof(mv->forceshader)); + +#ifdef RAGDOLL + menu->remove = M_Modelviewer_Shutdown; + mv->ragworld.progs = &mv->ragfuncs; + mv->ragfuncs.AddString = M_Modelviewer_AddString; + mv->ragfuncs.ProgsToEdict = M_Modelviewer_ProgsToEdict; + mv->ragfuncs.edicttable = (edict_t**)&mv->ragworld.edicts; + mv->ragworld.edicts = &mv->ragworldedict; + mv->ragworld.edicts->v = &mv->ragworldvars; + mv->ragworld.edicts->xv = &mv->ragworldextvars; + mv->ragworld.num_edicts = 1; + mv->ragworld.edicts->v->solid = SOLID_BBOX; + VectorSet(mv->ragworld.edicts->v->mins, -1000, -1000, -101); + VectorSet(mv->ragworld.edicts->v->maxs, 1000, 1000, -100); + + mv->ragworld.worldmodel = Mod_ForName("", MLV_SILENT); + World_RBE_Start(&mv->ragworld); +#endif } #else void M_Menu_ModelViewer_f(void) diff --git a/engine/client/merged.h b/engine/client/merged.h index 468d3ffe8..ef3ddd335 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -172,7 +172,10 @@ extern void Mod_TouchModel (const char *name); extern const char *Mod_FixName (const char *modname, const char *worldname); //remaps the name appropriately const char *Mod_ParseWorldspawnKey (struct model_s *mod, const char *key, char *buffer, size_t sizeofbuffer); +extern long relitsurface; +extern struct model_s *lightmodel; extern void Mod_Think (void); +extern qboolean Mod_GetModelEvent (struct model_s *model, int animation, int eventidx, float *timestamp, int *eventcode, char **eventdata); extern int Mod_SkinNumForName (struct model_s *model, int surfaceidx, const char *name); extern int Mod_FrameNumForName (struct model_s *model, int surfaceidx, const char *name); extern float Mod_GetFrameDuration (struct model_s *model, int surfaceidx, int framenum); @@ -183,6 +186,7 @@ extern int Mod_GetFrameCount (struct model_s *model); extern qboolean Mod_GetTag (struct model_s *model, int tagnum, framestate_t *framestate, float *transforms); extern int Mod_TagNumForName (struct model_s *model, const char *name); +void Mod_AddSingleSurface(struct entity_s *ent, int surfaceidx, shader_t *shader); int Mod_GetNumBones(struct model_s *model, qboolean allowtags); int Mod_GetBoneRelations(struct model_s *model, int firstbone, int lastbone, framestate_t *fstate, float *result); int Mod_GetBoneParent(struct model_s *model, int bonenum); diff --git a/engine/client/net_master.c b/engine/client/net_master.c index 002dbc292..dc9d33dca 100644 --- a/engine/client/net_master.c +++ b/engine/client/net_master.c @@ -17,7 +17,9 @@ void Master_DetermineMasterTypes(void) { if (com_protocolname.modified) { + char tok[MAX_QPATH]; char *prot = com_protocolname.string; + char *game; com_protocolname.modified = 0; sb_enabledarkplaces = true; //dpmaster is not specific to any single game/mod, so can be left enabled even when running q2 etc, for extra redundancy. @@ -26,19 +28,24 @@ void Master_DetermineMasterTypes(void) sb_enablenetquake = false; sb_enablequakeworld = false; - //this is stupid - if (!Q_strncasecmp(prot, "FTE-", 4)) - prot += 4; - else if (!Q_strncasecmp(prot, "DarkPlaces-", 11)) - prot += 11; + for (prot = com_protocolname.string; *prot;) + { + prot = COM_ParseOut(prot, tok, sizeof(tok)); + game = tok; + //this is stupid, but hey + if (!Q_strncasecmp(game, "FTE-", 4)) + game += 4; + else if (!Q_strncasecmp(game, "DarkPlaces-", 11)) + game += 11; - if (!strcmp(prot, "Quake2")) - sb_enablequake2 = true; - if (!strcmp(prot, "Quake3")) - sb_enablequake3 = true; - //for DP compatibility, we consider these separate(ish) games. - if (!strcmp(prot, "Quake") || !strcmp(com_protocolname.string, "Hipnotic") || !strcmp(com_protocolname.string, "Rogue")) - sb_enablenetquake = sb_enablequakeworld = true; + if (!strcmp(game, "Quake2")) + sb_enablequake2 = true; + if (!strcmp(game, "Quake3")) + sb_enablequake3 = true; + //for DP compatibility, we consider these separate(ish) games. + if (!strcmp(game, "Quake") || !strcmp(game, "Hipnotic") || !strcmp(game, "Rogue")) + sb_enablenetquake = sb_enablequakeworld = true; + } } } @@ -1974,7 +1981,7 @@ int Master_CheckPollSockets(void) s = MSG_ReadString(); old = Info_ValueForKey(info->moreinfo->info, selectedserver.lastrule); - if (strcmp(s, old)) + if (strcmp(s, old) && !strchr(s, '\"') && !strchr(s, '\\')) Info_SetValueForStarKey(info->moreinfo->info, selectedserver.lastrule, s, sizeof(info->moreinfo->info)); //... and now try to query the next one... because everyone gives up after the first, right?... dude... I hate this shit. @@ -2352,13 +2359,20 @@ void MasterInfo_Request(master_t *mast) case MP_DPMASTER: { char *str; - //for compat with dp, we use the nq netchan version. which is stupid, but whatever - //we ask for ipv6 addresses from ipv6 masters (assuming it resolved okay) - if (mast->adr.type == NA_IPV6) - str = va("%c%c%c%cgetserversExt %s %u empty full ipv6"/*\x0A\n"*/, 255, 255, 255, 255, com_protocolname.string, NQ_NETCHAN_VERSION); - else - str = va("%c%c%c%cgetservers %s %u empty full"/*\x0A\n"*/, 255, 255, 255, 255, com_protocolname.string, NQ_NETCHAN_VERSION); - NET_SendPollPacket (strlen(str), str, mast->adr); + char game[MAX_QPATH]; + char *games = com_protocolname.string; + while(*games) + { //send a request for each game listed. + games = COM_ParseOut(games, game, sizeof(game)); + + //for compat with dp, we use the nq netchan version. which is stupid, but whatever + //we ask for ipv6 addresses from ipv6 masters (assuming it resolved okay) + if (mast->adr.type == NA_IPV6) + str = va("%c%c%c%cgetserversExt %s %u empty full ipv6"/*\x0A\n"*/, 255, 255, 255, 255, game, NQ_NETCHAN_VERSION); + else + str = va("%c%c%c%cgetservers %s %u empty full"/*\x0A\n"*/, 255, 255, 255, 255, game, NQ_NETCHAN_VERSION); + NET_SendPollPacket (strlen(str), str, mast->adr); + } } break; #endif diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 59d73ecca..16a88da93 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -133,6 +133,7 @@ extern sfx_t *cl_sfx_r_exp3; globalfloat(frametime, "frametime"); /*float Client render frame interval*/ \ globalfloat(gamespeed, "gamespeed"); /*float Multiplier for real time -> simulation time*/ \ globalfloat(cltime, "cltime"); /*float Clientside map uptime indepent of gamespeed, latency, and the server in general*/ \ + globalfloat(clframetime, "clframetime"); /*float time since last video frame*/ \ globalfloat(netnewtime, "servertime"); /*float Server time of latest inbound network frame*/ \ globalfloat(netoldtime, "serverprevtime"); /*float Server time of previous inbound network frame */ \ globalfloat(netdeltatime, "serverdeltatime"); /*float new-old */ \ @@ -600,7 +601,7 @@ static void cs_getframestate(csqcedict_t *in, unsigned int rflags, framestate_t out->bonecount = 0; out->bonestate = NULL; if (in->xv->skeletonindex) - skel_lookup(csqcprogs, in->xv->skeletonindex, out); + skel_lookup(&csqc_world, in->xv->skeletonindex, out); #endif } @@ -1715,7 +1716,7 @@ static void QCBUILTIN PF_R_ClearScene (pubprogfuncs_t *prinst, struct globalvars CL_DecayLights (); #if defined(SKELETALOBJECTS) || defined(RAGDOLLS) - skel_dodelete(csqcprogs); + skel_dodelete(&csqc_world); #endif CL_ClearEntityLists(); @@ -3416,7 +3417,7 @@ static const char *PF_cs_getplayerkey_internal (unsigned int pnum, const char *k static char buffer[64]; char *ret; - if (pnum < 0 || pnum >= cl.allocated_client_slots) + if ((unsigned int)pnum >= (unsigned int)cl.allocated_client_slots) ret = ""; else if (!strcmp(keyname, "viewentity")) //compat with DP. Yes, I know this is in the wrong place. This is an evil hack. { @@ -4565,6 +4566,7 @@ static void CSQC_LerpStateToCSQC(lerpents_t *le, csqcedict_t *ent, qboolean nole ent->xv->baseframe2 = le->oldframe[FST_BASE]; ent->xv->baseframe2time = max(0, cl.servertime - le->oldframestarttime[FST_BASE]); ent->xv->baselerpfrac = bound(0, 1-(ent->xv->baseframe1time) / le->framelerpdeltatime[FST_BASE], 1); + ent->xv->basebone = le->basebone; if (nolerp) @@ -5156,9 +5158,6 @@ static void QCBUILTIN PF_V_CalcRefdef(pubprogfuncs_t *prinst, struct globalvars_ CL_DecayLights (); -#if defined(SKELETALOBJECTS) || defined(RAGDOLLS) - skel_dodelete(csqcprogs); -#endif CL_ClearEntityLists(); V_ClearRefdef(csqc_playerview); @@ -5706,6 +5705,9 @@ static struct { {"skel_delete", PF_skel_delete, 275},//void(float skel) skel_delete = #275; // (FTE_CSQC_SKELETONOBJECTS) {"frameforname", PF_frameforname, 276},//void(float modidx, string framename) frameforname = #276 (FTE_CSQC_SKELETONOBJECTS) {"frameduration", PF_frameduration, 277},//void(float modidx, float framenum) frameduration = #277 (FTE_CSQC_SKELETONOBJECTS) + {"processmodelevents", PF_processmodelevents, 0}, + {"getnextmodelevent", PF_getnextmodelevent, 0}, + {"getmodeleventidx", PF_getmodeleventidx, 0}, {"crossproduct", PF_crossproduct, 0}, @@ -6336,7 +6338,15 @@ static qboolean QDECL CSQC_Event_ContentsTransition(world_t *w, wedict_t *ent, i static model_t *QDECL CSQC_World_ModelForIndex(world_t *w, int modelindex) { - return CSQC_GetModelForIndex(modelindex); + model_t *mod = CSQC_GetModelForIndex(modelindex); + if (mod && mod->loadstate != MLS_LOADED) + { + if (mod->loadstate == MLS_LOADING) + COM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING); + if (mod->loadstate != MLS_LOADED) + mod = NULL; //gah, it failed! + } + return mod; } static void QDECL CSQC_World_GetFrameState(world_t *w, wedict_t *win, framestate_t *out) { @@ -7310,6 +7320,8 @@ qboolean CSQC_DrawView(void) else *csqcg.frametime = host_frametime; } + if (csqcg.clframetime) + *csqcg.clframetime = host_frametime; if (csqcg.numclientseats) *csqcg.numclientseats = cl.splitclients; diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index ca5cb4173..00b6d04c3 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -1838,7 +1838,9 @@ static void QCBUILTIN PF_m_clearscene(pubprogfuncs_t *prinst, struct globalvars_ // CL_DecayLights (); #if defined(SKELETALOBJECTS) || defined(RAGDOLLS) - skel_dodelete(prinst); + world_t *world = prinst->parms->user; + if (world) + skel_dodelete(world); #endif CL_ClearEntityLists(); diff --git a/engine/client/pr_skelobj.c b/engine/client/pr_skelobj.c index a85bb7d64..94741f3e5 100644 --- a/engine/client/pr_skelobj.c +++ b/engine/client/pr_skelobj.c @@ -482,6 +482,19 @@ static qboolean rag_dollline(dollcreatectx_t *ctx, int linenum) ctx->body->dimensions[1] = atof(Cmd_Argv(2)); ctx->body->dimensions[2] = atof(Cmd_Argv(3)); } + else if (ctx->body && argc == 4 && !stricmp(cmd, "offset")) + { + vec3_t ang; + ctx->body->isoffset = true; + ctx->body->relmatrix[3] = atof(val); + ctx->body->relmatrix[7] = atof(Cmd_Argv(2)); + ctx->body->relmatrix[11] = atof(Cmd_Argv(3)); + ang[0] = atof(Cmd_Argv(4)); + ang[1] = atof(Cmd_Argv(5)); + ang[2] = atof(Cmd_Argv(6)); + AngleVectorsFLU(ang, &ctx->body->relmatrix[0], &ctx->body->relmatrix[4], &ctx->body->relmatrix[8]); + Matrix3x4_Invert_Simple(ctx->body->relmatrix, ctx->body->inverserelmatrix); + } //joint properties else if (ctx->joint && argc == 2 && !stricmp(cmd, "type")) @@ -966,13 +979,13 @@ void skel_info_f(void) } /*destroys all skeletons*/ -void skel_reset(pubprogfuncs_t *prinst) +void skel_reset(world_t *world) { int i; for (i = 0; i < countof(skelobjects); i++) { - if (skelobjects[i].world == prinst->parms->user) + if (skelobjects[i].world == world) { #ifdef RAGDOLL rag_uninstanciate(&skelobjects[i]); @@ -992,7 +1005,7 @@ void skel_reset(pubprogfuncs_t *prinst) } /*deletes any skeletons marked for deletion*/ -void skel_dodelete(pubprogfuncs_t *prinst) +void skel_dodelete(world_t *world) { int skelidx; if (!pendingkill) @@ -1014,7 +1027,7 @@ void skel_dodelete(pubprogfuncs_t *prinst) numskelobjectsused--; } -static skelobject_t *skel_create(pubprogfuncs_t *prinst, int bonecount) +static skelobject_t *skel_create(world_t *world, int bonecount) { unsigned int skelidx; //invalid if the bonecount is not set... @@ -1023,7 +1036,7 @@ static skelobject_t *skel_create(pubprogfuncs_t *prinst, int bonecount) for (skelidx = 0; skelidx < numskelobjectsused; skelidx++) { - if (!skelobjects[skelidx].inuse && skelobjects[skelidx].numbones == bonecount && skelobjects[skelidx].world == prinst->parms->user) + if (!skelobjects[skelidx].inuse && skelobjects[skelidx].numbones == bonecount && skelobjects[skelidx].world == world) { skelobjects[skelidx].inuse = 1; return &skelobjects[skelidx]; @@ -1034,14 +1047,14 @@ static skelobject_t *skel_create(pubprogfuncs_t *prinst, int bonecount) { if (!skelobjects[skelidx].inuse && (!skelobjects[skelidx].numbones || skelobjects[skelidx].numbones == bonecount) && - (!skelobjects[skelidx].world || skelobjects[skelidx].world == prinst->parms->user)) + (!skelobjects[skelidx].world || skelobjects[skelidx].world == world)) { if (!skelobjects[skelidx].numbones) { skelobjects[skelidx].numbones = bonecount; - skelobjects[skelidx].bonematrix = (float*)PR_AddString(prinst, "", sizeof(float)*12*bonecount, false); + skelobjects[skelidx].bonematrix = (float*)PR_AddString(world->progs, "", sizeof(float)*12*bonecount, false); } - skelobjects[skelidx].world = prinst->parms->user; + skelobjects[skelidx].world = world; if (numskelobjectsused <= skelidx) numskelobjectsused = skelidx + 1; skelobjects[skelidx].model = NULL; @@ -1052,7 +1065,7 @@ static skelobject_t *skel_create(pubprogfuncs_t *prinst, int bonecount) return NULL; } -static skelobject_t *skel_get(pubprogfuncs_t *prinst, int skelidx) +static skelobject_t *skel_get(world_t *world, int skelidx) { skelidx--; if ((unsigned int)skelidx >= numskelobjectsused) @@ -1062,9 +1075,9 @@ static skelobject_t *skel_get(pubprogfuncs_t *prinst, int skelidx) return &skelobjects[skelidx]; } -void skel_lookup(pubprogfuncs_t *prinst, int skelidx, framestate_t *out) +void skel_lookup(world_t *world, int skelidx, framestate_t *out) { - skelobject_t *sko = skel_get(prinst, skelidx); + skelobject_t *sko = skel_get(world, skelidx); if (sko && sko->inuse) { out->skeltype = sko->type; @@ -1075,9 +1088,10 @@ void skel_lookup(pubprogfuncs_t *prinst, int skelidx, framestate_t *out) void QCBUILTIN PF_skel_mmap(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { + world_t *world = prinst->parms->user; int skelidx = G_FLOAT(OFS_PARM0); - skelobject_t *sko = skel_get(prinst, skelidx); - if (!sko || sko->world != prinst->parms->user) + skelobject_t *sko = skel_get(world, skelidx); + if (!sko || sko->world != world) G_INT(OFS_RETURN) = 0; else G_INT(OFS_RETURN) = (char*)sko->bonematrix - prinst->stringtable; @@ -1118,9 +1132,17 @@ static void rag_uninstanciate(skelobject_t *sko) } static void rag_genbodymatrix(skelobject_t *sko, rbebodyinfo_t *dollbody, float *emat, float *result) { + float tmp[12]; float *bmat; int bone = dollbody->bone; bmat = sko->bonematrix + bone*12; + + if (dollbody->isoffset) + { + R_ConcatTransforms((void*)dollbody->relmatrix, (void*)bmat, (void*)tmp); + bmat = tmp; + } + R_ConcatTransforms((void*)emat, (void*)bmat, (void*)result); if (dollbody->orient) @@ -1331,8 +1353,15 @@ static void rag_derive(skelobject_t *sko, skelobject_t *asko, float *emat) { //bones with a body are given an absolute pose matching that body. sko->world->rbe->RagMatrixFromBody(sko->world, &sko->body[doll->bone[i].bodyidx].odebody, bodymat); - //that body matrix is in world space, so transform to model space for our result - R_ConcatTransforms((void*)invemat, (void*)bodymat, (void*)((float*)bmat+i*12)); + if (doll->body[doll->bone[i].bodyidx].isoffset) + { + float tmp[12]; + R_ConcatTransforms((void*)doll->body[doll->bone[i].bodyidx].inverserelmatrix, (void*)bodymat, (void*)tmp); + R_ConcatTransforms((void*)invemat, (void*)tmp, (void*)((float*)bmat+i*12)); + } + else + //that body matrix is in world space, so transform to model space for our result + R_ConcatTransforms((void*)invemat, (void*)bodymat, (void*)((float*)bmat+i*12)); } else if (amat) //FIXME: don't do this when the bone has an unanimated child body. { @@ -1399,7 +1428,7 @@ void rag_removedeltaent(lerpents_t *le) return; le->skeletalobject = 0; - skelobj = skel_get(csqc_world.progs, skelidx); + skelobj = skel_get(&csqc_world, skelidx); if (skelobj) { skelobj->inuse = 2; //2 means don't reuse yet. @@ -1421,7 +1450,7 @@ void rag_lerpdeltaent(lerpents_t *le, unsigned int bonecount, short *newstate, f skelobject_t *sko; if (le->skeletalobject) { - sko = skel_get(csqc_world.progs, le->skeletalobject); + sko = skel_get(&csqc_world, le->skeletalobject); if (sko->numbones != bonecount) { //unusable, discard it and create a new one. sko->inuse = 2; //2 means don't reuse yet. @@ -1435,7 +1464,7 @@ void rag_lerpdeltaent(lerpents_t *le, unsigned int bonecount, short *newstate, f if (!sko || sko->inuse != 1) { - sko = skel_create(csqc_world.progs, bonecount); + sko = skel_create(&csqc_world, bonecount); if (!sko) return; //couldn't get one, ran out of memory or something? sko->model = NULL; @@ -1480,10 +1509,8 @@ void rag_lerpdeltaent(lerpents_t *le, unsigned int bonecount, short *newstate, f } } -void rag_updatedeltaent(entity_t *ent, lerpents_t *le) +void rag_updatedeltaent(world_t *w, entity_t *ent, lerpents_t *le) { - extern world_t csqc_world; - world_t *w; model_t *mod = ent->model; skelobject_t *sko; float emat[12]; @@ -1494,13 +1521,12 @@ void rag_updatedeltaent(entity_t *ent, lerpents_t *le) if (mod->dollinfo) { - w = &csqc_world; if (!w->rbe) return; if (!le->skeletalobject) { - sko = skel_create(w->progs, Mod_GetNumBones(mod, false)); + sko = skel_create(w, Mod_GetNumBones(mod, false)); if (!sko) return; //couldn't get one, ran out of memory or something? sko->model = mod; @@ -1509,7 +1535,7 @@ void rag_updatedeltaent(entity_t *ent, lerpents_t *le) } else { - sko = skel_get(w->progs, le->skeletalobject); + sko = skel_get(w, le->skeletalobject); if (!sko) { le->skeletalobject = 0; @@ -1551,8 +1577,7 @@ void rag_updatedeltaent(entity_t *ent, lerpents_t *le) } else if (le->skeletalobject) { - w = &csqc_world; - sko = skel_get(w->progs, le->skeletalobject); + sko = skel_get(w, le->skeletalobject); if (!sko) { le->skeletalobject = 0; @@ -1574,6 +1599,7 @@ void QCBUILTIN PF_skel_ragedit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g { //do we want to be able to generate a ragdoll object with this function too? #ifdef RAGDOLL + world_t *w = prinst->parms->user; wedict_t *wed = (wedict_t*)G_EDICT(prinst, OFS_PARM0); const char *ragname = PR_GetStringOfs(prinst, OFS_PARM1); int parentskel = G_FLOAT(OFS_PARM2); @@ -1594,11 +1620,11 @@ void QCBUILTIN PF_skel_ragedit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g G_FLOAT(OFS_RETURN) = 0; //the parent skeletal object must be relative, if specified. - psko = skel_get(prinst, parentskel); + psko = skel_get(w, parentskel); if (psko && psko->type != SKEL_RELATIVE) return; - sko = skel_get(prinst, skelidx); + sko = skel_get(w, skelidx); if (!sko) { Con_DPrintf("PF_skel_ragedit: invalid skeletal object\n"); @@ -1747,7 +1773,7 @@ void QCBUILTIN PF_skel_create (pubprogfuncs_t *prinst, struct globalvars_s *pr_g return; //this isn't a skeletal model. } - skelobj = skel_create(prinst, numbones); + skelobj = skel_create(w, numbones); if (!skelobj) return; //couldn't get one, ran out of memory or something? @@ -1804,9 +1830,9 @@ void QCBUILTIN PF_skel_build(pubprogfuncs_t *prinst, struct globalvars_s *pr_glo } if (!skelidx) - skelobj = skel_create(prinst, numbones); + skelobj = skel_create(w, numbones); else - skelobj = skel_get(prinst, skelidx); + skelobj = skel_get(w, skelidx); if (!skelobj) return; //couldn't get one, ran out of memory or something? @@ -1888,10 +1914,11 @@ void QCBUILTIN PF_skel_build(pubprogfuncs_t *prinst, struct globalvars_s *pr_glo //float(float skel) skel_get_numbones (FTE_CSQC_SKELETONOBJECTS) void QCBUILTIN PF_skel_get_numbones (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { + world_t *w = prinst->parms->user; int skelidx = G_FLOAT(OFS_PARM0); skelobject_t *skelobj; - skelobj = skel_get(prinst, skelidx); + skelobj = skel_get(w, skelidx); if (!skelobj) G_FLOAT(OFS_RETURN) = 0; @@ -1902,11 +1929,12 @@ void QCBUILTIN PF_skel_get_numbones (pubprogfuncs_t *prinst, struct globalvars_s //string(float skel, float bonenum) skel_get_bonename (FTE_CSQC_SKELETONOBJECTS) (returns tempstring) void QCBUILTIN PF_skel_get_bonename (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { + world_t *w = prinst->parms->user; int skelidx = G_FLOAT(OFS_PARM0); int boneidx = G_FLOAT(OFS_PARM1); skelobject_t *skelobj; - skelobj = skel_get(prinst, skelidx); + skelobj = skel_get(w, skelidx); if (!skelobj) G_INT(OFS_RETURN) = 0; @@ -1919,11 +1947,12 @@ void QCBUILTIN PF_skel_get_bonename (pubprogfuncs_t *prinst, struct globalvars_s //float(float skel, float bonenum) skel_get_boneparent (FTE_CSQC_SKELETONOBJECTS) void QCBUILTIN PF_skel_get_boneparent (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { + world_t *w = prinst->parms->user; int skelidx = G_FLOAT(OFS_PARM0); int boneidx = G_FLOAT(OFS_PARM1); skelobject_t *skelobj; - skelobj = skel_get(prinst, skelidx); + skelobj = skel_get(w, skelidx); if (!skelobj) G_FLOAT(OFS_RETURN) = 0; @@ -1934,11 +1963,12 @@ void QCBUILTIN PF_skel_get_boneparent (pubprogfuncs_t *prinst, struct globalvars //float(float skel, string tagname) skel_find_bone (FTE_CSQC_SKELETONOBJECTS) void QCBUILTIN PF_skel_find_bone (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { + world_t *w = prinst->parms->user; int skelidx = G_FLOAT(OFS_PARM0); const char *bname = PR_GetStringOfs(prinst, OFS_PARM1); skelobject_t *skelobj; - skelobj = skel_get(prinst, skelidx); + skelobj = skel_get(w, skelidx); if (!skelobj) G_FLOAT(OFS_RETURN) = 0; else @@ -1951,7 +1981,7 @@ void QCBUILTIN PF_skel_get_bonerel (pubprogfuncs_t *prinst, struct globalvars_s world_t *w = prinst->parms->user; int skelidx = G_FLOAT(OFS_PARM0); int boneidx = G_FLOAT(OFS_PARM1)-1; - skelobject_t *skelobj = skel_get(prinst, skelidx); + skelobject_t *skelobj = skel_get(w, skelidx); if (!skelobj || (unsigned int)boneidx >= skelobj->numbones) bonematident_toqcvectors(w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_RETURN)); else if (skelobj->type!=SKEL_RELATIVE) @@ -1977,7 +2007,7 @@ void QCBUILTIN PF_skel_get_boneabs (pubprogfuncs_t *prinst, struct globalvars_s int boneidx = G_FLOAT(OFS_PARM1)-1; float workingm[12], tempmatrix[3][4]; int i; - skelobject_t *skelobj = skel_get(prinst, skelidx); + skelobject_t *skelobj = skel_get(w, skelidx); if (!skelobj || (unsigned int)boneidx >= skelobj->numbones) bonematident_toqcvectors(w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_RETURN)); @@ -2050,7 +2080,7 @@ void QCBUILTIN PF_skel_set_bone_world (pubprogfuncs_t *prinst, struct globalvars } /*make sure the skeletal object is correct*/ - skelobj = skel_get(prinst, ent->xv->skeletonindex); + skelobj = skel_get(w, ent->xv->skeletonindex); if (!skelobj || boneidx >= skelobj->numbones) return; @@ -2101,7 +2131,7 @@ void QCBUILTIN PF_skel_set_bone (pubprogfuncs_t *prinst, struct globalvars_s *pr matrix[2] = w->g.v_up; } - skelobj = skel_get(prinst, skelidx); + skelobj = skel_get(w, skelidx); if (!skelobj || boneidx >= skelobj->numbones) return; @@ -2123,7 +2153,7 @@ void QCBUILTIN PF_skel_mul_bone (pubprogfuncs_t *prinst, struct globalvars_s *pr else bonemat_fromqcvectors((float*)mult, w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_PARM2)); - skelobj = skel_get(prinst, skelidx); + skelobj = skel_get(w, skelidx); if (!skelobj || boneidx >= skelobj->numbones) return; //testme @@ -2148,7 +2178,7 @@ void QCBUILTIN PF_skel_mul_bones (pubprogfuncs_t *prinst, struct globalvars_s *p else bonemat_fromqcvectors((float*)mult, w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_PARM3)); - skelobj = skel_get(prinst, skelidx); + skelobj = skel_get(w, skelidx); if (!skelobj) return; @@ -2173,6 +2203,7 @@ void QCBUILTIN PF_skel_mul_bones (pubprogfuncs_t *prinst, struct globalvars_s *p //void(float skeldst, float skelsrc, float startbone, float entbone) skel_copybones (FTE_CSQC_SKELETONOBJECTS) void QCBUILTIN PF_skel_copybones (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { + world_t *w = prinst->parms->user; int skeldst = G_FLOAT(OFS_PARM0); int skelsrc = G_FLOAT(OFS_PARM1); int startbone = G_FLOAT(OFS_PARM2)-1; @@ -2181,8 +2212,8 @@ void QCBUILTIN PF_skel_copybones (pubprogfuncs_t *prinst, struct globalvars_s *p skelobject_t *skelobjdst; skelobject_t *skelobjsrc; - skelobjdst = skel_get(prinst, skeldst); - skelobjsrc = skel_get(prinst, skelsrc); + skelobjdst = skel_get(w, skeldst); + skelobjsrc = skel_get(w, skelsrc); if (!skelobjdst || !skelobjsrc) return; if (startbone == -1) @@ -2220,10 +2251,11 @@ void QCBUILTIN PF_skel_copybones (pubprogfuncs_t *prinst, struct globalvars_s *p //void(float skel) skel_delete (FTE_CSQC_SKELETONOBJECTS) void QCBUILTIN PF_skel_delete (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { + world_t *w = prinst->parms->user; int skelidx = G_FLOAT(OFS_PARM0); skelobject_t *skelobj; - skelobj = skel_get(prinst, skelidx); + skelobj = skel_get(w, skelidx); if (skelobj) { skelobj->inuse = 2; //2 means don't reuse yet. @@ -2344,6 +2376,238 @@ void QCBUILTIN PF_modelframecount (pubprogfuncs_t *prinst, struct globalvars_s * G_FLOAT(OFS_RETURN) = 0; } +//void(float modidx, float framenum, __inout float basetime, float targettime, void(float timestamp, int code, string data) callback) +void QCBUILTIN PF_processmodelevents (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + world_t *w = prinst->parms->user; + unsigned int modelindex = G_FLOAT(OFS_PARM0); + unsigned int frame = G_FLOAT(OFS_PARM1); + float basetime = G_FLOAT(OFS_PARM2); + float targettime = G_FLOAT(OFS_PARM3); + func_t callback = G_INT(OFS_PARM4); + model_t *mod = w->Get_CModel(w, modelindex); + float starttime, timestamp; + + if (targettime == basetime) + return; //don't refire the same event multiple times. + + //returns all basetime <= eventtime < targettime + + if (mod) + { + if (mod->type == mod_alias) + { //slightly more optimised path that is kinda redundant, but w/e + galiasinfo_t *ga = Mod_Extradata(mod); + galiasanimation_t *anim = ga->ofsanimations + frame; + galiasevent_t *ev; + float loopduration; + if (frame < (unsigned int)ga->numanimations && anim->events) + { + if (anim->loop) + { + loopduration = anim->rate * anim->numposes; + starttime = loopduration*(unsigned int)(basetime/loopduration); + } + else + starttime = loopduration = 0; + for (ev = anim->events; ; ) + { + //be careful to use as consistent timings as we can + timestamp = starttime + ev->timestamp; + if (timestamp >= targettime) + break; //this is in the future. + if (timestamp >= basetime) + { + G_FLOAT(OFS_PARM0) = timestamp; + G_INT(OFS_PARM1) = ev->code; + G_INT(OFS_PARM2) = PR_TempString(prinst, ev->data); + PR_ExecuteProgram(prinst, callback); + } + + ev = ev->next; + if (!ev) + { + if (loopduration) + ev = anim->events; + else + break; //animation ends here, so no more events possible + starttime += loopduration; + } + } + } + } +#ifdef HALFLIFEMODELS + else //actually this is a generic version that would work for iqm etc too, but is less efficient due to repeated lookups. oh well. + { + int ev, code; + char *data; + float loopduration; + qboolean looping; + + if (Mod_FrameInfoForNum(mod, 0, frame, &data, &code, &loopduration, &looping)) + { + if (looping && loopduration) + starttime = loopduration*(unsigned int)(basetime/loopduration); + else + starttime = loopduration = 0; + for (ev = 0; ; ev++) + { + if (!Mod_GetModelEvent(mod, frame, ev, ×tamp, &code, &data)) + { + if (looping && Mod_GetModelEvent(mod, frame, 0, ×tamp, &code, &data)) + { + ev = 0; + starttime += loopduration; + } + else + break; //end of anim + } + + //be careful to use as consistent timings as we can... + timestamp += starttime; + if (timestamp >= targettime) + break; //this is in the future. + if (timestamp >= basetime) + { + G_FLOAT(OFS_PARM0) = timestamp; + G_INT(OFS_PARM1) = code; + G_INT(OFS_PARM2) = PR_TempString(prinst, data); + PR_ExecuteProgram(prinst, callback); + } + } + } + } +#endif + } + G_FLOAT(OFS_PARM2) = targettime; +} + +//float(float modidx, float framenum, __inout float basetime, float targettime, __out int code, __out string data) +void QCBUILTIN PF_getnextmodelevent (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + world_t *w = prinst->parms->user; + unsigned int modelindex = G_FLOAT(OFS_PARM0); + unsigned int frame = G_FLOAT(OFS_PARM1); + float basetime = G_FLOAT(OFS_PARM2); + float targettime = G_FLOAT(OFS_PARM3); + model_t *mod = w->Get_CModel(w, modelindex); + float starttime, timestamp; + //default return values + G_FLOAT(OFS_RETURN) = false; + G_FLOAT(OFS_PARM2) = targettime; + G_INT(OFS_PARM4) = 0; + G_INT(OFS_PARM5) = 0; + + if (mod) + { + if (mod->type == mod_alias) + { //slightly more optimised path that is kinda redundant, but w/e + galiasinfo_t *ga = Mod_Extradata(mod); + galiasanimation_t *anim = ga->ofsanimations + frame; + galiasevent_t *ev; + float loopduration; + if (frame >= (unsigned int)ga->numanimations || !anim->events) + return; + if (anim->loop) + { + loopduration = anim->rate * anim->numposes; + starttime = loopduration*(unsigned int)(basetime/loopduration); + } + else + starttime = loopduration = 0; + for (ev = anim->events; ; ) + { + //be careful to use as consistent timings as we can + timestamp = starttime + ev->timestamp; + if (timestamp > targettime) + break; //this is in the future. + if (timestamp > basetime) + { + G_FLOAT(OFS_RETURN) = true; + G_FLOAT(OFS_PARM2) = timestamp; + G_INT(OFS_PARM4) = ev->code; + G_INT(OFS_PARM5) = PR_TempString(prinst, ev->data); + return; + } + + ev = ev->next; + if (!ev) + { + if (loopduration) + ev = anim->events; + else + return; //animation ended here, so no more events + starttime += loopduration; + } + } + } +#ifdef HALFLIFEMODELS + else //actually this is a generic version that would work for iqm etc too, but is less efficient due to repeated lookups. oh well. + { + int ev, code; + char *data; + float loopduration; + qboolean looping; + + if (!Mod_FrameInfoForNum(mod, 0, frame, &data, &code, &loopduration, &looping)) + return; //invalid frame + + if (looping && loopduration) + starttime = loopduration*(unsigned int)(basetime/loopduration); + else + starttime = loopduration = 0; + for (ev = 0; ; ev++) + { + if (!Mod_GetModelEvent(mod, frame, ev, ×tamp, &code, &data)) + { + if (looping && Mod_GetModelEvent(mod, frame, 0, ×tamp, &code, &data)) + { + ev = 0; + starttime += loopduration; + } + else + break; //end of anim + } + + //be careful to use as consistent timings as we can... + timestamp += starttime; + if (timestamp > targettime) + break; //this is in the future. + if (timestamp > basetime) + { + G_FLOAT(OFS_RETURN) = true; + G_FLOAT(OFS_PARM2) = timestamp; + G_INT(OFS_PARM4) = code; + G_INT(OFS_PARM5) = PR_TempString(prinst, data); + return; + } + } + } +#endif + } +} +//float(float modidx, float framenum, int idx, __out float timestamp, __out int code, __out string data) +void QCBUILTIN PF_getmodeleventidx (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + world_t *w = prinst->parms->user; + unsigned int modelindex = G_FLOAT(OFS_PARM0); + unsigned int frame = G_FLOAT(OFS_PARM1); + int eventindex = G_INT(OFS_PARM2); + model_t *mod = w->Get_CModel(w, modelindex); + //default return values + float timestamp = 0; + int code = 0; + char *data = NULL; + + G_FLOAT(OFS_RETURN) = Mod_GetModelEvent(mod, frame, eventindex, ×tamp, &code, &data); + G_FLOAT(OFS_PARM3) = timestamp; + G_INT(OFS_PARM4) = code; + if (data) + G_INT(OFS_PARM5) = PR_TempString(prinst, data); + else + G_INT(OFS_PARM5) = 0; +} + //string(float modidx, float skinnum) skintoname void QCBUILTIN PF_skintoname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { diff --git a/engine/client/r_2d.c b/engine/client/r_2d.c index 41425a86a..246a1c0d9 100644 --- a/engine/client/r_2d.c +++ b/engine/client/r_2d.c @@ -660,6 +660,7 @@ void R2D_Image2dQuad(vec2_t points[], vec2_t texcoords[], mpic_t *pic) { Vector2Copy(points[i], draw_mesh_xyz[i]); Vector2Copy(texcoords[i], draw_mesh_st[i]); + Vector4Copy(draw_active_colour, draw_mesh_colors[i]); } BE_DrawMesh_Single(pic, &draw_mesh, NULL, r2d_be_flags); diff --git a/engine/client/r_part.c b/engine/client/r_part.c index ed115444b..e6b285062 100644 --- a/engine/client/r_part.c +++ b/engine/client/r_part.c @@ -1160,7 +1160,7 @@ void P_DefaultTrail (unsigned int entityeffects, unsigned int modelflags, int *t *trailid = P_FindParticleType("tr_scarab"); *trailpalidx = 254; } - else if (modelflags & MFH2_ROCKET) + else if (modelflags & MFH2_SPIDERBLOOD) { //spiders *trailid = P_FindParticleType("TR_GREENBLOOD"); diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index ce25cd12d..79680ff93 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -1889,7 +1889,7 @@ start: Surf_RecursiveWorldNode (node->children[side], clipflags); // draw stuff - c = node->numsurfaces; + c = node->numsurfaces; if (c) { @@ -2907,6 +2907,8 @@ void R_GenWorldEBO(void *ctx, void *data, size_t a, size_t b) } else #endif +#ifdef Q1BSPS + if (es->wmodel->fromgame == fg_quake || es->wmodel->fromgame == fg_halflife) { //maybe we should just use fatpvs instead, and wait for completion when outside? if (es->leaf[1]) @@ -2926,6 +2928,11 @@ void R_GenWorldEBO(void *ctx, void *data, size_t a, size_t b) } Surf_SimpleWorld_Q1BSP(es, pvs); } + else +#endif + { + //panic + } COM_AddWork(WG_MAIN, R_GeneratedWorldEBO, es, NULL, 0, 0); } @@ -2963,12 +2970,12 @@ void Surf_DrawWorld (void) { RSpeedRemark(); - Surf_LightmapShift(cl.worldmodel); + Surf_LightmapShift(currentmodel); #ifdef THREADEDWORLD - if ((r_dynamic.ival < 0 || cl.worldmodel->numbatches) && !r_refdef.recurse && cl.worldmodel->type == mod_brush) + if ((r_dynamic.ival < 0 || currentmodel->numbatches) && !r_refdef.recurse && currentmodel->type == mod_brush) { - if (webostate && webostate->wmodel != cl.worldmodel) + if (webostate && webostate->wmodel != currentmodel) { R_DestroyWorldEBO(webostate); webostate = NULL; @@ -2976,7 +2983,8 @@ void Surf_DrawWorld (void) if (qrenderer != QR_OPENGL && qrenderer != QR_VULKAN) ; - else if (cl.worldmodel->fromgame == fg_quake) +#ifdef Q1BSPS + else if (currentmodel->fromgame == fg_quake || currentmodel->fromgame == fg_halflife) { int i = MAX_LIGHTSTYLES; if (webostate && !webogenerating) @@ -2993,21 +3001,21 @@ void Surf_DrawWorld (void) if (!webogenerating) { int i; - if (!cl.worldmodel->numbatches) + if (!currentmodel->numbatches) { int sortid; batch_t *batch; - cl.worldmodel->numbatches = 0; + currentmodel->numbatches = 0; for (sortid = 0; sortid < SHADER_SORT_COUNT; sortid++) - for (batch = cl.worldmodel->batches[sortid]; batch != NULL; batch = batch->next) + for (batch = currentmodel->batches[sortid]; batch != NULL; batch = batch->next) { - batch->ebobatch = cl.worldmodel->numbatches; - cl.worldmodel->numbatches++; + batch->ebobatch = currentmodel->numbatches; + currentmodel->numbatches++; } } webogeneratingstate = true; - webogenerating = BZ_Malloc(sizeof(*webogenerating) + sizeof(webogenerating->batches[0]) * (cl.worldmodel->numbatches-1)); - webogenerating->wmodel = cl.worldmodel; + webogenerating = BZ_Malloc(sizeof(*webogenerating) + sizeof(webogenerating->batches[0]) * (currentmodel->numbatches-1)); + webogenerating->wmodel = currentmodel; webogenerating->leaf[0] = r_viewleaf; webogenerating->leaf[1] = r_viewleaf2; for (i = 0; i < MAX_LIGHTSTYLES; i++) @@ -3017,7 +3025,9 @@ void Surf_DrawWorld (void) } } } - else if (cl.worldmodel->fromgame == fg_quake3) +#endif +#ifdef Q3BSPS + else if (currentmodel->fromgame == fg_quake3) { if (webostate && webostate->cluster[0] == r_viewcluster && webostate->cluster[1] == r_viewcluster2) { @@ -3026,21 +3036,21 @@ void Surf_DrawWorld (void) { if (!webogenerating) { - if (!cl.worldmodel->numbatches) + if (!currentmodel->numbatches) { int sortid; batch_t *batch; - cl.worldmodel->numbatches = 0; + currentmodel->numbatches = 0; for (sortid = 0; sortid < SHADER_SORT_COUNT; sortid++) - for (batch = cl.worldmodel->batches[sortid]; batch != NULL; batch = batch->next) + for (batch = currentmodel->batches[sortid]; batch != NULL; batch = batch->next) { - batch->ebobatch = cl.worldmodel->numbatches; - cl.worldmodel->numbatches++; + batch->ebobatch = currentmodel->numbatches; + currentmodel->numbatches++; } } webogeneratingstate = true; - webogenerating = BZ_Malloc(sizeof(*webogenerating) + sizeof(webogenerating->batches[0]) * (cl.worldmodel->numbatches-1)); - webogenerating->wmodel = cl.worldmodel; + webogenerating = BZ_Malloc(sizeof(*webogenerating) + sizeof(webogenerating->batches[0]) * (currentmodel->numbatches-1)); + webogenerating->wmodel = currentmodel; webogenerating->cluster[0] = r_viewcluster; webogenerating->cluster[1] = r_viewcluster2; Q_strncpyz(webogenerating->dbgid, "webostate", sizeof(webogenerating->dbgid)); @@ -3048,6 +3058,7 @@ void Surf_DrawWorld (void) } } } +#endif if (webostate) { @@ -3065,7 +3076,7 @@ void Surf_DrawWorld (void) BE_DrawWorld(webostate->rbatches); /*FIXME: move this away*/ - if (cl.worldmodel->fromgame == fg_quake || cl.worldmodel->fromgame == fg_halflife) + if (currentmodel->fromgame == fg_quake || currentmodel->fromgame == fg_halflife) Surf_LessenStains(); return; } @@ -3073,26 +3084,34 @@ void Surf_DrawWorld (void) #endif - Surf_PushChains(cl.worldmodel->batches); + Surf_PushChains(currentmodel->batches); +#ifdef TERRAIN + if (currentmodel->type == mod_heightmap) + { + frustumvis = NULL; + entvis = surfvis = NULL; + } + else +#endif #ifdef Q2BSPS - if (cl.worldmodel->fromgame == fg_quake2 || cl.worldmodel->fromgame == fg_quake3) + if (currentmodel->fromgame == fg_quake2 || currentmodel->fromgame == fg_quake3) { frustumvis = frustumvis_; - memset(frustumvis, 0, (cl.worldmodel->numclusters + 7)>>3); + memset(frustumvis, 0, (currentmodel->numclusters + 7)>>3); if (!r_refdef.areabitsknown) { //generate the info each frame, as the gamecode didn't tell us what to use. - int leafnum = CM_PointLeafnum (cl.worldmodel, r_refdef.vieworg); - int clientarea = CM_LeafArea (cl.worldmodel, leafnum); - CM_WriteAreaBits(cl.worldmodel, r_refdef.areabits, clientarea, false); + int leafnum = CM_PointLeafnum (currentmodel, r_refdef.vieworg); + int clientarea = CM_LeafArea (currentmodel, leafnum); + CM_WriteAreaBits(currentmodel, r_refdef.areabits, clientarea, false); r_refdef.areabitsknown = true; } #ifdef Q3BSPS - if (cl.worldmodel->fromgame == fg_quake3) + if (currentmodel->fromgame == fg_quake3) { entvis = surfvis = R_MarkLeaves_Q3 (); - Surf_RecursiveQ3WorldNode (cl.worldmodel->nodes, (1<nodes, (1<nodes); + Surf_RecursiveQ2WorldNode (currentmodel->nodes); } surfvis = frustumvis; @@ -3108,9 +3127,9 @@ void Surf_DrawWorld (void) else #endif #ifdef MAP_PROC - if (cl.worldmodel->fromgame == fg_doom3) + if (currentmodel->fromgame == fg_doom3) { - entvis = surfvis = D3_CalcVis(cl.worldmodel, r_origin); + entvis = surfvis = D3_CalcVis(currentmodel, r_origin); } else #endif @@ -3122,14 +3141,8 @@ void Surf_DrawWorld (void) } else #endif -#ifdef TERRAIN - if (currentmodel->type == mod_heightmap) - { - frustumvis = NULL; - entvis = surfvis = NULL; - } - else -#endif +#ifdef Q1BSPS + if (1) { //extern cvar_t temp1; // if (0)//temp1.value) @@ -3141,15 +3154,21 @@ void Surf_DrawWorld (void) VectorCopy (r_origin, modelorg); frustumvis = frustumvis_; - memset(frustumvis, 0, (cl.worldmodel->numclusters + 7)>>3); + memset(frustumvis, 0, (currentmodel->numclusters + 7)>>3); if (r_refdef.useperspective) - Surf_RecursiveWorldNode (cl.worldmodel->nodes, 0x1f); + Surf_RecursiveWorldNode (currentmodel->nodes, 0x1f); else - Surf_OrthoRecursiveWorldNode (cl.worldmodel->nodes, 0x1f); + Surf_OrthoRecursiveWorldNode (currentmodel->nodes, 0x1f); surfvis = frustumvis; } } + else +#endif + { + frustumvis = NULL; + entvis = surfvis = NULL; + } RSpeedEnd(RSPEED_WORLDNODE); diff --git a/engine/client/renderer.c b/engine/client/renderer.c index eab48044c..51ab85986 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -1046,6 +1046,7 @@ rendererinfo_t swrendererinfo; #endif #ifdef VKQUAKE rendererinfo_t vkrendererinfo; +//rendererinfo_t headlessvkrendererinfo; #endif rendererinfo_t headlessrenderer; @@ -1074,6 +1075,9 @@ rendererinfo_t *rendererinfo[] = &dedicatedrendererinfo, #endif &headlessrenderer, +#ifdef VKQUAKE + //&headlessvkrendererinfo, +#endif }; @@ -1863,7 +1867,7 @@ void R_SetRenderer_f (void) for (i = 0; i < sizeof(rendererinfo)/sizeof(rendererinfo[0]); i++) { if (rendererinfo[i]->description) - Con_Printf("^1%s^7: %s%s\n", rendererinfo[i]->name[0], rendererinfo[i]->description, (currentrendererstate.renderer == rendererinfo[i])?" ^2(current)":""); + Con_Printf("^[%s\\type\\/setrenderer %s^]^7: %s%s\n", rendererinfo[i]->name[0], rendererinfo[i]->name[0], rendererinfo[i]->description, (currentrendererstate.renderer == rendererinfo[i])?" ^2(current)":""); } return; } @@ -1880,7 +1884,7 @@ void R_SetRenderer_f (void) Cvar_Set(&vid_bpp, Cmd_Argv(2)); } - if (newr.renderer->rtype != QR_HEADLESS) //don't save headless in the vid_renderer cvar via the setrenderer command. 'setrenderer headless;vid_restart' can then do what is most sane. + if (newr.renderer->rtype != QR_HEADLESS && !strstr(param, "headless")) //don't save headless in the vid_renderer cvar via the setrenderer command. 'setrenderer headless;vid_restart' can then do what is most sane. Cvar_Set(&vid_renderer, param); if (!r_blockvidrestart) @@ -2279,6 +2283,7 @@ qbyte *R_MarkLeaves_Q2 (void) } #endif +#ifdef Q1BSPS #if 0 qbyte *R_CalcVis_Q1 (void) { @@ -2420,6 +2425,7 @@ qbyte *R_MarkLeaves_Q1 (qboolean getvisonly) } return vis; } +#endif /* ================= diff --git a/engine/client/snd_al.c b/engine/client/snd_al.c index 87a234e4d..d8682c151 100644 --- a/engine/client/snd_al.c +++ b/engine/client/snd_al.c @@ -321,8 +321,8 @@ extern cvar_t snd_doppler; typedef struct { - #define NUM_SOURCES MAX_CHANNELS - ALuint source[NUM_SOURCES]; + ALuint *source; + size_t max_sources; ALCdevice *OpenAL_Device; ALCcontext *OpenAL_Context; @@ -582,8 +582,12 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned int chnum = chan - sc->channel; ALuint buf; - if (chnum >= NUM_SOURCES) + if (chnum >= oali->max_sources) + { + size_t nc = chnum+1+64; + Z_ReallocElements((void**)&oali->source, &oali->max_sources, nc, sizeof(*oali->source)); return; + } //alcMakeContextCurrent @@ -966,7 +970,7 @@ static qboolean OpenAL_Init(soundcardinfo_t *sc, const char *devname) //S_Info(); //fixme... - memset(oali->source, 0, sizeof(oali->source)); + memset(oali->source, 0, sizeof(*oali->source)*oali->max_sources); PrintALError("alGensources for normal sources"); palListenerfv(AL_POSITION, oali->ListenPos); @@ -1107,7 +1111,7 @@ static void OpenAL_Shutdown (soundcardinfo_t *sc) //alcMakeContextCurrent - palDeleteSources(NUM_SOURCES, oali->source); + palDeleteSources(oali->max_sources, oali->source); /*make sure the buffers are cleared from the sound effects*/ for (i=0;ichannel[i].sfx->name, sc->channel[i].entnum, sc->channel[i].entchannel, sc->channel[i].origin[0], sc->channel[i].origin[1], sc->channel[i].origin[2]); } } - Con_Printf(" %d/%d/%d/%d active/known/highest/max\n", active, known, sc->total_chans, MAX_CHANNELS); + Con_Printf(" %d/%d/%d/%d active/known/highest/max\n", active, known, sc->total_chans, sc->max_chans); for (i = 0; i < sc->sn.numchannels; i++) { Con_Printf(" chan %i: fwd:%-4g rt:%-4g up:%-4g dist:%-4g\n", i, sc->speakerdir[i][0], sc->speakerdir[i][1], sc->speakerdir[i][2], sc->dist[i]); @@ -2845,9 +2845,10 @@ void S_StopAllSounds(qboolean clear) } sc->total_chans = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS + NUM_MUSICS; // no statics + Z_ReallocElements((void**)&sc->channel, &sc->max_chans, sc->total_chans, sizeof(*sc->channel)); memcpy(musics, &sc->channel[MUSIC_FIRST], sizeof(musics)); - Q_memset(sc->channel, 0, MAX_CHANNELS * sizeof(channel_t)); + Q_memset(sc->channel, 0, sc->max_chans * sizeof(channel_t)); memcpy(&sc->channel[MUSIC_FIRST], musics, sizeof(musics)); if (clear && !sc->selfpainting) //if its self-painting, then the mixer will continue painting anyway (which is important if its still painting music, but otherwise don't stutter at all when loading) @@ -2903,10 +2904,13 @@ void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation) for (scard = sndcardinfo; scard; scard = scard->next) { - if (scard->total_chans == MAX_CHANNELS) + if (scard->total_chans == scard->max_chans) { - Con_Printf ("total_channels == MAX_CHANNELS\n"); - continue; + if (!ZF_ReallocElements((void**)&scard->channel, &scard->max_chans, scard->max_chans+64, sizeof(*scard->channel))) + { + Con_Printf ("total_channels == MAX_CHANNELS\n"); + continue; + } } if (!S_LoadSound (sfx)) @@ -3004,11 +3008,14 @@ S_UpdateAmbientSounds mleaf_t *Q1BSP_LeafForPoint (model_t *model, vec3_t p); void S_UpdateAmbientSounds (soundcardinfo_t *sc) { - mleaf_t *l; - float vol, oldvol; + float vol; channel_t *chan; int i; +#ifdef Q1BSPS + mleaf_t *l; + float oldvol; int ambientlevel[NUM_AMBIENTS]; +#endif if (!snd_ambient) return; @@ -3073,7 +3080,7 @@ void S_UpdateAmbientSounds (soundcardinfo_t *sc) } } - +#ifdef Q1BSPS // calc ambient sound levels for (i = 0; i < NUM_AMBIENTS; i++) ambientlevel[i] = 0; @@ -3140,6 +3147,7 @@ void S_UpdateAmbientSounds (soundcardinfo_t *sc) if (sc->ChannelUpdate) sc->ChannelUpdate(sc, chan, (oldvol == 0) ^ (sc->ambientlevels[i] == 0)); } +#endif } struct sndreverbproperties_s *reverbproperties; diff --git a/engine/client/sound.h b/engine/client/sound.h index d95f9b9b8..3dc78f15c 100644 --- a/engine/client/sound.h +++ b/engine/client/sound.h @@ -255,7 +255,6 @@ void OpenAL_CvarInit(void); // User-setable variables // ==================================================================== -#define MAX_CHANNELS 1024/*tracked sounds (including statics)*/ #define MAX_DYNAMIC_CHANNELS 64 /*playing sounds (identical ones merge)*/ @@ -331,8 +330,9 @@ struct soundcardinfo_s { //windows has one defined AFTER directsound //info on which sound effects are playing //FIXME: use a linked list - channel_t channel[MAX_CHANNELS]; + channel_t *channel; int total_chans; + int max_chans; float ambientlevels[NUM_AMBIENTS]; //we use a float instead of the channel's int volume value to avoid framerate dependancies with slow transitions. diff --git a/engine/client/sys_win.c b/engine/client/sys_win.c index 9753e5609..9e96ef7a2 100644 --- a/engine/client/sys_win.c +++ b/engine/client/sys_win.c @@ -367,12 +367,13 @@ void Sys_CloseLibrary(dllhandle_t *lib) } HMODULE LoadLibraryU(const char *name) { + HMODULE ret; if (WinNT) { wchar_t wide[MAX_OSPATH]; widen(wide, sizeof(wide), name); - return LoadLibraryW(wide); + ret = LoadLibraryW(wide); } else { @@ -380,8 +381,9 @@ HMODULE LoadLibraryU(const char *name) char ansi[MAX_OSPATH]; widen(wide, sizeof(wide), name); ansi[WideCharToMultiByte(CP_ACP, 0, wide, wcslen(wide), ansi, sizeof(ansi)-1, NULL, NULL)] = 0; - return LoadLibraryA(ansi); + ret = LoadLibraryA(ansi); } + return ret; } dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs) { @@ -4186,7 +4188,7 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin { isDedicated = isClusterSlave = true; #ifdef _DEBUG - MessageBox(0, "Cluster slave", "gah", 0); + MessageBox(0, "New cluster slave starting\nAttach to process now, if desired.", "FTEQW", 0); #endif } #endif diff --git a/engine/client/vid_headless.c b/engine/client/vid_headless.c index 45086716c..c8f805776 100644 --- a/engine/client/vid_headless.c +++ b/engine/client/vid_headless.c @@ -281,4 +281,112 @@ rendererinfo_t headlessrenderer = Headless_BE_RenderToTextureUpdate2d, "" }; + + + + +#if 0//def VKQUAKE +#include "../vk/vkrenderer.h" +static qboolean HeadlessVK_CreateSurface(void) +{ + vk.surface = VK_NULL_HANDLE; //nothing to create, we're using offscreen rendering. + return true; +} +//static void HeadlessVK_Present(struct vkframe *theframe) +//{ + //VK_DoPresent(theframe); +//} +static qboolean HeadlessVK_Init (rendererstate_t *info, unsigned char *palette) +{ + extern cvar_t vid_conautoscale; +#ifdef VK_NO_PROTOTYPES + static dllhandle_t *hInstVulkan = NULL; + dllfunction_t vkfuncs[] = + { + {(void**)&vkGetInstanceProcAddr, "vkGetInstanceProcAddr"}, + {NULL} + }; + if (!hInstVulkan) + hInstVulkan = *info->subrenderer?Sys_LoadLibrary(info->subrenderer, vkfuncs):NULL; +#ifdef _WIN32 + if (!hInstVulkan) + hInstVulkan = Sys_LoadLibrary("vulkan-1.dll", vkfuncs); +#else + if (!hInstVulkan) + hInstVulkan = Sys_LoadLibrary("libvulkan.so.1", vkfuncs); + if (!hInstVulkan) + hInstVulkan = Sys_LoadLibrary("libvulkan.so", vkfuncs); +#endif + if (!hInstVulkan) + { + Con_Printf("Unable to load vulkan library\nNo Vulkan drivers are installed\n"); + return false; + } +#endif + + vid.pixelwidth = 1920; + vid.pixelheight = 1080; + if (!VK_Init(info, NULL, HeadlessVK_CreateSurface, NULL)) + return false; + Cvar_ForceCallback(&vid_conautoscale); + return true; +} + +rendererinfo_t headlessvkrendererinfo = +{ + "Headless Vulkan", + { + "vkheadless" + }, + QR_VULKAN, + + VK_Draw_Init, + VK_Draw_Shutdown, + + VK_UpdateFiltering, + VK_LoadTextureMips, + VK_DestroyTexture, + + VK_R_Init, + VK_R_DeInit, + VK_R_RenderView, + + HeadlessVK_Init, + GLVID_DeInit, + GLVID_SwapBuffers, + GLVID_ApplyGammaRamps, + WIN_CreateCursor, + WIN_SetCursor, + WIN_DestroyCursor, + GLVID_SetCaption, + VKVID_GetRGBInfo, + + VK_SCR_UpdateScreen, + + VKBE_SelectMode, + VKBE_DrawMesh_List, + VKBE_DrawMesh_Single, + VKBE_SubmitBatch, + VKBE_GetTempBatch, + VKBE_DrawWorld, + VKBE_Init, + VKBE_GenBrushModelVBO, + VKBE_ClearVBO, + VKBE_UploadAllLightmaps, + VKBE_SelectEntity, + VKBE_SelectDLight, + VKBE_Scissor, + VKBE_LightCullModel, + + VKBE_VBO_Begin, + VKBE_VBO_Data, + VKBE_VBO_Finish, + VKBE_VBO_Destroy, + + VKBE_RenderToTextureUpdate2d, + + "no more" +}; +#endif + #endif diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index 18152e21d..b53f598b7 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -173,6 +173,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define SPRMODELS //quake1 sprite models #define INTERQUAKEMODELS #define RTLIGHTS //realtime lighting + #define Q1BSPS //quake 1 bsp support, because we're still a quake engine #define Q2BSPS //quake 2 bsp support (a dependancy of q3bsp) #define Q3BSPS //quake 3 bsp support // #define TERRAIN //heightmap support @@ -234,6 +235,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #define DSPMODELS //doom sprites (only needs DOOMWADS to generate the right wad file names) #define SPRMODELS //quake1 sprite models #define SP2MODELS //quake2 sprite models + #define MD1MODELS //quake1 alias models #define MD2MODELS //quake2 alias models #define MD3MODELS //quake3 alias models #define MD5MODELS //doom3 models @@ -249,6 +251,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #define MAP_DOOM //doom map support #define MAP_PROC //doom3/quake4 map support //#define WOLF3DSUPPORT //wolfenstein3d map support (not started yet) + #define Q1BSPS //quake 1 bsp support, because we're still a quake engine #define Q2BSPS //quake 2 bsp support #define Q3BSPS //quake 3 bsp support #define TERRAIN //heightmap support diff --git a/engine/common/bspfile.h b/engine/common/bspfile.h index 039691244..512701a81 100644 --- a/engine/common/bspfile.h +++ b/engine/common/bspfile.h @@ -552,7 +552,7 @@ typedef struct #define Q2CONTENTS_TRANSLUCENT 0x10000000 // auto set if any surface has trans #define Q2CONTENTS_LADDER 0x20000000 //0x40000000 - //0x80000000 + //FTECONTENTS_SKY //0x80000000 #define Q3CONTENTS_SOLID FTECONTENTS_SOLID //0x00000001 // should never be on a brush, only in game @@ -580,7 +580,7 @@ typedef struct #define Q3CONTENTS_BOTCLIP 0x00400000 #define Q3CONTENTS_MOVER 0x00800000 #define Q3CONTENTS_ORIGIN Q2CONTENTS_ORIGIN //0x01000000 -#define Q3CONTENTS_BODY 0x02000000 +#define Q3CONTENTS_BODY FTECONTENTS_BODY //0x02000000 #define Q3CONTENTS_CORPSE FTECONTENTS_CORPSE //0x04000000 #define Q3CONTENTS_DETAIL Q2CONTENTS_DETAIL //0x08000000 #define Q3CONTENTS_STRUCTURAL 0x10000000 diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index 6aaf94997..7d4acd09f 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -2,6 +2,14 @@ #include "com_mesh.h" +#ifdef __F16C__ +#ifdef _MSC_VER + #include +#else + #include +#endif +#endif + qboolean r_loadbumpmapping; extern cvar_t dpcompat_psa_ungroup; extern cvar_t r_noframegrouplerp; @@ -1251,6 +1259,7 @@ static void Alias_BuildSkeletalMesh(mesh_t *mesh, framestate_t *framestate, gali ); } +#if !defined(SERVERONLY) static void Alias_BuildSkeletalVerts(float *xyzout, framestate_t *framestate, galiasinfo_t *inf) { float buffer[MAX_BONES*12]; @@ -1261,6 +1270,7 @@ static void Alias_BuildSkeletalVerts(float *xyzout, framestate_t *framestate, ga Alias_TransformVerticies_V(bonepose, inf->numverts, bidx, weight, inf->ofs_skel_xyz[0], xyzout); } +#endif #if defined(MD5MODELS) || defined(ZYMOTICMODELS) || defined(DPMMODELS) static int QDECL sortweights(const void *v1, const void *v2) //helper for Alias_BuildGPUWeights @@ -1901,9 +1911,120 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in return true; //to allow the mesh to be dlighted. } +#ifndef SERVERONLY +//used by the modelviewer. +void Mod_AddSingleSurface(entity_t *ent, int surfaceidx, shader_t *shader) +{ + galiasinfo_t *mod; + galiasanimation_t *group; + galiaspose_t *pose; + scenetris_t *t; + vecV_t *posedata = NULL; + int surfnum = 0, i; +#ifdef SKELETALMODELS + int cursurfnum = -1; +#endif + if (!ent->model || ent->model->loadstate != MLS_LOADED || ent->model->type != mod_alias) + return; + mod = Mod_Extradata(ent->model); + for(; mod; mod = mod->nextsurf, surfnum++) + { + if (surfaceidx < 0) + { + if (!mod->contents) + continue; + } + else if (surfnum != surfaceidx) + continue; +#ifdef SKELETALMODELS + if (mod->numbones) + { + if (!mod->ofs_skel_idx) + posedata = mod->ofs_skel_xyz; //if there's no weights, don't try animating anything. + else if (mod->shares_verts != cursurfnum || !posedata) + { + cursurfnum = mod->shares_verts; + + posedata = alloca(mod->numverts*sizeof(vecV_t)); + Alias_BuildSkeletalVerts((float*)posedata, &ent->framestate, mod); + } + //else posedata = posedata; + } + else +#endif + if (!mod->numanimations) + continue; + else + { + group = mod->ofsanimations; + group += ent->framestate.g[FS_REG].frame[0] % mod->numanimations; + //FIXME: no support for frame blending. + if (!group->numposes || !group->poseofs) + continue; + pose = group->poseofs; + pose += (int)(ent->framestate.g[FS_REG].frametime[0] * group->rate)%group->numposes; + posedata = pose->ofsverts; + } + + if (cl_numstris == cl_maxstris) + { + cl_maxstris+=8; + cl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris); + } + t = &cl_stris[cl_numstris++]; + t->shader = shader; + t->flags = 0;//BEF_LINES; + t->firstidx = cl_numstrisidx; + t->firstvert = cl_numstrisvert; + if (t->flags&BEF_LINES) + t->numidx = mod->numindexes*2; + else + t->numidx = mod->numindexes; + t->numvert = mod->numverts; + + if (cl_numstrisidx+t->numidx > cl_maxstrisidx) + { + cl_maxstrisidx=cl_numstrisidx+t->numidx; + cl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx); + } + if (cl_numstrisvert+mod->numverts > cl_maxstrisvert) + { + cl_maxstrisvert=cl_numstrisvert+mod->numverts; + cl_strisvertv = BZ_Realloc(cl_strisvertv, sizeof(*cl_strisvertv)*cl_maxstrisvert); + cl_strisvertt = BZ_Realloc(cl_strisvertt, sizeof(*cl_strisvertt)*cl_maxstrisvert); + cl_strisvertc = BZ_Realloc(cl_strisvertc, sizeof(*cl_strisvertc)*cl_maxstrisvert); + } + for (i = 0; i < mod->numverts; i++) + { + VectorAdd(ent->origin, posedata[i], cl_strisvertv[t->firstvert+i]); + Vector2Set(cl_strisvertt[t->firstvert+i], 0.5, 0.5); + Vector4Set(cl_strisvertc[t->firstvert+i], (mod->contents?1:0), 1, 1, 0.1); + } + if (t->flags&BEF_LINES) + { + for (i = 0; i < mod->numindexes; i+=3) + { + cl_strisidx[cl_numstrisidx++] = mod->ofs_indexes[i+0]; + cl_strisidx[cl_numstrisidx++] = mod->ofs_indexes[i+1]; + cl_strisidx[cl_numstrisidx++] = mod->ofs_indexes[i+1]; + cl_strisidx[cl_numstrisidx++] = mod->ofs_indexes[i+2]; + cl_strisidx[cl_numstrisidx++] = mod->ofs_indexes[i+2]; + cl_strisidx[cl_numstrisidx++] = mod->ofs_indexes[i+0]; + } + } + else + { + for (i = 0; i < mod->numindexes; i++) + cl_strisidx[cl_numstrisidx+i] = mod->ofs_indexes[i]; + cl_numstrisidx += mod->numindexes; + } + cl_numstrisvert += mod->numverts; + } +} +#endif static float PlaneNearest(vec3_t normal, vec3_t mins, vec3_t maxs) @@ -2111,11 +2232,14 @@ qboolean Mod_Trace(model_t *model, int forcehullnum, framestate_t *framestate, v // float temp; - vecV_t *posedata; + vecV_t *posedata = NULL; index_t *indexes; int surfnum = 0; #ifdef SKELETALMODELS - int cursurfnum = -1; + int cursurfnum = -1, curbonesurf = -1; + float buffer[MAX_BONES*12]; + float bufferalt[MAX_BONES*12]; + const float *bonepose = NULL; #endif vec3_t start_l, end_l; @@ -2139,19 +2263,27 @@ qboolean Mod_Trace(model_t *model, int forcehullnum, framestate_t *framestate, v for(; mod; mod = mod->nextsurf, surfnum++) { + if (!(mod->contents & contentsmask)) + continue; //this mesh isn't solid to the trace + indexes = mod->ofs_indexes; #ifdef SKELETALMODELS if (mod->numbones) { if (!mod->ofs_skel_idx) posedata = mod->ofs_skel_xyz; //if there's no weights, don't try animating anything. - else if (mod->shares_verts != cursurfnum) + else if (mod->shares_verts != cursurfnum || !posedata) { cursurfnum = mod->shares_verts; - + if (curbonesurf != mod->shares_bones) + { + curbonesurf = mod->shares_bones; + bonepose = Alias_GetBoneInformation(mod, framestate, SKEL_INVERSE_ABSOLUTE, buffer, bufferalt, MAX_BONES); + } posedata = alloca(mod->numverts*sizeof(vecV_t)); - Alias_BuildSkeletalVerts((float*)posedata, framestate, mod); + Alias_TransformVerticies_V(bonepose, mod->numverts, mod->ofs_skel_idx[0], mod->ofs_skel_weight[0], mod->ofs_skel_xyz[0], posedata[0]); } + //else posedata = posedata; } else #endif @@ -2169,10 +2301,11 @@ qboolean Mod_Trace(model_t *model, int forcehullnum, framestate_t *framestate, v posedata = pose->ofsverts; } - trace->truefraction = 1; if (Mod_Trace_Trisoup(posedata, indexes, mod->numindexes, start_l, end_l, mins, maxs, trace)) { - trace->surface_id = 1+surfnum; + trace->contents = mod->contents; + trace->surface = &mod->csurface; + trace->surface_id = mod->surfaceid; trace->bone_id = 0; #ifdef SKELETALMODELS if (mod->ofs_skel_weight) @@ -2248,7 +2381,7 @@ static void Mod_ClampModelSize(model_t *mod) if (mod->engineflags & MDLF_DOCRC) { if (!strcmp(mod->name, "progs/eyes.mdl")) - { //this is checked elsewhere to make sure the crc matches (this is to make sure the crc check was actually called) + { //this is checked elsewhere to make sure the crc matches (this is to make sure the crc check was actually called) if (mod->type != mod_alias || mod->fromgame != fg_quake || mod->flags) mod->tainted = true; } @@ -2388,6 +2521,18 @@ static frameinfo_t *ParseFrameInfo(char *modelname, int *numgroups) return frames; } +void Mod_DefaultMesh(galiasinfo_t *galias, const char *name, unsigned int index) +{ + Q_strncpyz(galias->surfacename, name, sizeof(galias->surfacename)); + Q_strncpyz(galias->csurface.name, COM_SkipPath(name), sizeof(galias->csurface.name)); + galias->contents = FTECONTENTS_BODY; + galias->geomset = ~0; //invalid set = always visible + galias->geomid = 0; + galias->mindist = 0; + galias->maxdist = 0; + galias->surfaceid = index+1; +} + void Mod_DestroyMesh(galiasinfo_t *galias) { #ifndef SERVERONLY @@ -2736,7 +2881,12 @@ void Mod_LoadAliasShaders(model_t *mod) if (!f->shader) { if (!f->defaultshader) - f->shader = R_RegisterSkin(f->shadername, mod->name); + { + if (ai->csurface.flags & 0x80) //nodraw + f->shader = R_RegisterShader(f->shadername, SUF_NONE, "{\nsurfaceparm nodraw\nsurfaceparm nodlight\nsurfaceparm nomarks\nsurfaceparm noshadows\n}\n"); + else + f->shader = R_RegisterSkin(f->shadername, mod->name); + } else f->shader = R_RegisterShader(f->shadername, SUF_NONE, f->defaultshader); } @@ -2771,7 +2921,7 @@ void Mod_LoadAliasShaders(model_t *mod) //Q1 model loading -#if 1 +#ifdef MD1MODELS #define NUMVERTEXNORMALS 162 extern float r_avertexnormals[NUMVERTEXNORMALS][3]; // mdltype 0 = q1, 1 = qtest, 2 = rapo/h2 @@ -3207,7 +3357,68 @@ static void *Q1MDL_LoadSkins_GL (galiasinfo_t *galias, dmdl_t *pq1inmodel, model } #endif -void Mesh_HandleFramegroupsFile(model_t *mod, galiasinfo_t *galias) +//parses a foo.mdl.events file and inserts the events into the relevant animations +static void Mod_InsertEvent(zonegroup_t *mem, galiasanimation_t *anims, unsigned int numanimations, unsigned int eventanimation, float eventpose, int eventcode, const char *eventdata) +{ + galiasevent_t *ev, **link; + if (eventanimation >= numanimations) + { + Con_Printf("Mod_InsertEvent: invalid frame index\n"); + return; + } + ev = ZG_Malloc(mem, sizeof(*ev) + strlen(eventdata)+1); + ev->data = (char*)(ev+1); + + ev->timestamp = eventpose; + ev->timestamp /= anims[eventanimation].rate; + ev->code = eventcode; + strcpy(ev->data, eventdata); + link = &anims[eventanimation].events; + while (*link && (*link)->timestamp <= ev->timestamp) + link = &(*link)->next; + ev->next = *link; + *link = ev; +} +static qboolean Mod_ParseModelEvents(model_t *mod, galiasanimation_t *anims, unsigned int numanimations) +{ + unsigned int anim; + float pose; + int eventcode; + + const char *modelname = mod->name; + zonegroup_t *mem = &mod->memgroup; + char fname[MAX_QPATH], tok[2048]; + size_t fsize; + char *line, *file, *eol; + Q_snprintfz(fname, sizeof(fname), "%s.events", modelname); + line = file = COM_LoadFile(fname, 5, &fsize); + if (!file) + return false; + while(line && *line) + { + eol = strchr(line, '\n'); + if (eol) + *eol = 0; + + line = COM_ParseOut(line, tok, sizeof(tok)); + anim = strtoul(tok, NULL, 0); + line = COM_ParseOut(line, tok, sizeof(tok)); + pose = strtod(tok, NULL); + line = COM_ParseOut(line, tok, sizeof(tok)); + eventcode = (long)strtol(tok, NULL, 0); + line = COM_ParseOut(line, tok, sizeof(tok)); + Mod_InsertEvent(mem, anims, numanimations, anim, pose, eventcode, tok); + + if (eol) + line = eol+1; + else + break; + } + BZ_Free(file); + return true; +} + +static void Mesh_HandleFramegroupsFile(model_t *mod, galiasinfo_t *galias) { unsigned int numanims, a, p, g, oldnumanims = galias->numanimations, targpose; galiasanimation_t *o, *oldanims = galias->ofsanimations, *frame; @@ -3241,7 +3452,7 @@ void Mesh_HandleFramegroupsFile(model_t *mod, galiasinfo_t *galias) } } -qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) +static qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) { #ifndef SERVERONLY vec2_t *st_array; @@ -3324,6 +3535,7 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) + pq1inmodel->numframes*sizeof(galiasanimation_t); galias = ZG_Malloc(&mod->memgroup, size); + Mod_DefaultMesh(galias, mod->name, 0); galias->ofsanimations = (galiasanimation_t*)(galias+1); #ifndef SERVERONLY galias->ofsskins = (galiasskin_t*)(galias->ofsanimations+pq1inmodel->numframes); @@ -3547,6 +3759,7 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) mod->type = mod_alias; Mod_ClampModelSize(mod); + Mod_ParseModelEvents(mod, galias->ofsanimations, galias->numanimations); Mesh_HandleFramegroupsFile(mod, galias); @@ -3571,15 +3784,13 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) galias->lerpcutoff = 30; #ifdef HEXEN2 if ((mod->flags == MF_ROCKET) && !strncmp(mod->name, "models/sflesh", 13)) - mod->flags = MFH2_ROCKET; + mod->flags = MFH2_SPIDERBLOOD; #endif return true; } -#endif - -int Mod_ReadFlagsFromMD1(char *name, int md3version) +static int Mod_ReadFlagsFromMD1(char *name, int md3version) { int result = 0; size_t fsize; @@ -3605,6 +3816,12 @@ int Mod_ReadFlagsFromMD1(char *name, int md3version) } return result; } +#else +static int Mod_ReadFlagsFromMD1(char *name, int md3version) +{ + return 0; +} +#endif #ifdef MD2MODELS @@ -3732,6 +3949,7 @@ qboolean QDECL Mod_LoadQ2Model (model_t *mod, void *buffer, size_t fsize) + LittleLong(pq2inmodel->num_frames)*sizeof(galiasanimation_t); galias = ZG_Malloc(&mod->memgroup, size); + Mod_DefaultMesh(galias, mod->name, 0); galias->ofsanimations = (galiasanimation_t*)(galias+1); #ifndef SERVERONLY galias->ofsskins = (galiasskin_t*)(galias->ofsanimations + LittleLong(pq2inmodel->num_frames)); @@ -3887,6 +4105,7 @@ qboolean QDECL Mod_LoadQ2Model (model_t *mod, void *buffer, size_t fsize) */ Mod_ClampModelSize(mod); + Mod_ParseModelEvents(mod, galias->ofsanimations, galias->numanimations); mod->meshinfo = galias; mod->numframes = galias->numanimations; @@ -4321,6 +4540,36 @@ int Mod_FrameNumForName(model_t *model, int surfaceidx, const char *name) return -1; } + +qboolean Mod_GetModelEvent(model_t *model, int animation, int eventidx, float *timestamp, int *eventcode, char **eventdata) +{ + if (!model) + return false; + if (model->type == mod_alias) + { + galiasinfo_t *inf = Mod_Extradata(model); + if (inf && animation >= 0 && animation < inf->numanimations) + { + galiasanimation_t *anim = inf->ofsanimations + animation; + galiasevent_t *ev = anim->events; + while (eventidx-->0 && ev) + ev = ev->next; + if (ev) + { + *timestamp = ev->timestamp; + *eventcode = ev->code; + *eventdata = ev->data; + return true; + } + } + } +#ifdef HALFLIFEMODELS + if (model->type == mod_halflife) + return HLMDL_GetModelEvent(model, animation, eventidx, timestamp, eventcode, eventdata); +#endif + return false; +} + #ifndef SERVERONLY int Mod_SkinNumForName(model_t *model, int surfaceidx, const char *name) { @@ -4647,6 +4896,7 @@ qboolean QDECL Mod_LoadQ3Model(model_t *mod, void *buffer, size_t fsize) Con_Printf(CON_WARNING "Warning: md3 sub-surface doesn't match ident\n"); size = sizeof(galiasinfo_t) + sizeof(galiasanimation_t)*LittleLong(header->numFrames); galias = ZG_Malloc(&mod->memgroup, size); + Mod_DefaultMesh(galias, surf->name, s); galias->ofsanimations = (galiasanimation_t*)(galias+1); //frame groups galias->numanimations = LittleLong(header->numFrames); galias->numverts = LittleLong(surf->numVerts); @@ -4658,8 +4908,6 @@ qboolean QDECL Mod_LoadQ3Model(model_t *mod, void *buffer, size_t fsize) root = galias; parent = galias; - Q_strncpyz(galias->surfacename, surf->name, sizeof(galias->surfacename)); - #ifndef SERVERONLY st_array = ZG_Malloc(&mod->memgroup, sizeof(vec2_t)*galias->numindexes); galias->ofs_st_array = st_array; @@ -4779,7 +5027,10 @@ qboolean QDECL Mod_LoadQ3Model(model_t *mod, void *buffer, size_t fsize) } if (!root) + { root = ZG_Malloc(&mod->memgroup, sizeof(galiasinfo_t)); + Mod_DefaultMesh(root, mod->name, 0); + } root->numtagframes = LittleLong(header->numFrames); root->numtags = LittleLong(header->numTags); @@ -4822,6 +5073,7 @@ qboolean QDECL Mod_LoadQ3Model(model_t *mod, void *buffer, size_t fsize) mod->flags = Mod_ReadFlagsFromMD1(mod->name, 0); Mod_ClampModelSize(mod); + Mod_ParseModelEvents(mod, root->ofsanimations, root->numanimations); mod->type = mod_alias; mod->numframes = root->numanimations; @@ -5067,11 +5319,10 @@ qboolean QDECL Mod_LoadZymoticModel(model_t *mod, void *buffer, size_t fsize) for (i = 0; i < header->numsurfaces; i++, surfname+=32) { + Mod_DefaultMesh(&root[i], surfname, i); root[i].numanimations = header->numscenes; root[i].ofsanimations = grp; - Q_strncpyz(root[i].surfacename, surfname, sizeof(root[i].surfacename)); - #ifdef SERVERONLY root[i].numskins = 1; #else @@ -5129,6 +5380,7 @@ qboolean QDECL Mod_LoadZymoticModel(model_t *mod, void *buffer, size_t fsize) mod->flags = Mod_ReadFlagsFromMD1(mod->name, 0); //file replacement - inherit flags from any defunc mdl files. Mod_ClampModelSize(mod); + Mod_ParseModelEvents(mod, root->ofsanimations, root->numanimations); mod->meshinfo = root; @@ -5528,6 +5780,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) } gmdl = ZG_Malloc(&mod->memgroup, sizeof(*gmdl)*num_matt); + Mod_DefaultMesh(gmdl, mod->name, 0); /*bones!*/ bones = ZG_Malloc(&mod->memgroup, sizeof(galiasbone_t) * num_boneinfo); @@ -5804,6 +6057,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) mod->flags = Mod_ReadFlagsFromMD1(mod->name, 0); //file replacement - inherit flags from any defunc mdl files. Mod_ClampModelSize(mod); + Mod_ParseModelEvents(mod, gmdl->ofsanimations, gmdl->numanimations); mod->meshinfo = gmdl; @@ -6026,6 +6280,7 @@ qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t fsize) for (i = 0; i < header->num_meshs; i++, mesh++) { m = &root[i]; + Mod_DefaultMesh(m, mesh->shadername, i); if (i < header->num_meshs-1) m->nextsurf = &root[i+1]; m->shares_bones = 0; @@ -6036,9 +6291,6 @@ qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t fsize) m->numanimations = header->num_frames; m->ofsanimations = outgroups; - - Q_strncpyz(m->surfacename, mesh->shadername, sizeof(m->surfacename)); - #ifdef SERVERONLY m->numskins = 1; #else @@ -6146,6 +6398,7 @@ qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t fsize) mod->flags = Mod_ReadFlagsFromMD1(mod->name, 0); //file replacement - inherit flags from any defunc mdl files. Mod_ClampModelSize(mod); + Mod_ParseModelEvents(mod, root->ofsanimations, root->numanimations); mod->meshinfo = root; mod->numframes = root->numanimations; @@ -6274,6 +6527,46 @@ struct iqmbounds float xyradius, radius; }; +struct iqmextension +{ + unsigned int name; + unsigned int num_data, ofs_data; + unsigned int ofs_extensions; // pointer to next extension. wtf is up with this? how is this not redundant due to ofs_data? +}; + + +struct iqmext_fte_mesh +{ + unsigned int contents; //default CONTENTS_BODY + unsigned int surfaceflags; //propagates to trace_surfaceflags + unsigned int surfaceid; //the body reported to qc via trace_surface + unsigned int geomset; + unsigned int geomid; + float mindist; + float maxdist; +}; +struct iqmext_fte_event +{ + unsigned int anim; + float timestamp; + unsigned int evcode; + unsigned int evdata_str; //stringtable +}; +/*struct iqmext_fte_shader +{ + unsigned int material; + unsigned int defaultshaderbody; + unsigned int ofs_data; //rgba or 8bit, depending on whether a palette is specified + unsigned int width; //if ofs_data + unsigned int height; //if ofs_data + unsigned int ofs_palette; //rgba*256. colourmapped if exactly matches quake's palette +}; +//struct iqmext_fte_ragdoll; // pure text +struct iqmext_fte_bone +{ + unsigned int bonegroup; +};*/ + /* galisskeletaltransforms_t *IQM_ImportTransforms(int *resultcount, int inverts, float *vpos, float *tcoord, float *vnorm, float *vtang, unsigned char *vbone, unsigned char *vweight) { @@ -6304,7 +6597,7 @@ galisskeletaltransforms_t *IQM_ImportTransforms(int *resultcount, int inverts, f } */ -static qboolean IQM_ImportArray4B(qbyte *base, struct iqmvertexarray *src, byte_vec4_t *out, size_t count, unsigned int maxval) +static qboolean IQM_ImportArray4B(const qbyte *base, const struct iqmvertexarray *src, byte_vec4_t *out, size_t count, unsigned int maxval) { size_t i; unsigned int j; @@ -6398,7 +6691,7 @@ static qboolean IQM_ImportArray4B(qbyte *base, struct iqmvertexarray *src, byte_ return !invalid; } -static void IQM_ImportArrayF(qbyte *base, struct iqmvertexarray *src, float *out, size_t e, size_t count, float *def) +static void IQM_ImportArrayF(const qbyte *base, const struct iqmvertexarray *src, float *out, size_t e, size_t count, float *def) { size_t i; unsigned int j; @@ -6415,9 +6708,9 @@ static void IQM_ImportArrayF(qbyte *base, struct iqmvertexarray *src, float *out default: sz = 0; break; - case IQM_BYTE: //FIXME: should be signed + case IQM_BYTE: //negatives not handled properly { - char *in = (qbyte*)(base+offset); + signed char *in = (signed char*)(base+offset); for (i = 0; i < count; i++) { for (j = 0; j < e && j < sz; j++) @@ -6435,9 +6728,9 @@ static void IQM_ImportArrayF(qbyte *base, struct iqmvertexarray *src, float *out } } break; - case IQM_SHORT: + case IQM_SHORT: //negatives not handled properly { - short *in = (short*)(base+offset); + signed short *in = (signed short*)(base+offset); for (i = 0; i < count; i++) { for (j = 0; j < e && j < sz; j++) @@ -6455,28 +6748,50 @@ static void IQM_ImportArrayF(qbyte *base, struct iqmvertexarray *src, float *out } } break; - case IQM_INT://FIXME: should be signed + case IQM_INT: //negatives not handled properly + { + signed int *in = (signed int*)(base+offset); + for (i = 0; i < count; i++) + { + for (j = 0; j < e && j < sz; j++) + out[i*e+j] = in[i*sz+j]/(float)0x7fffffff; + } + } + break; case IQM_UINT: { unsigned int *in = (unsigned int*)(base+offset); for (i = 0; i < count; i++) { for (j = 0; j < e && j < sz; j++) - out[i*e+j] = in[i*sz+j]; + out[i*e+j] = in[i*sz+j]/(float)0xffffffffu; } } break; - /*case IQM_HALF: + case IQM_HALF: +#ifdef __F16C__ + { //x86 intrinsics + unsigned short *in = (qbyte*)(base+offset); + for (i = 0; i < count; i++) + { + for (j = 0; j < e && j < sz; j++) + out[i*e+j] = _cvtsh_ss(in[i*sz+j]); + } + } +#elif 0 { - __fp16 *in = (qbyte*)(base+offset); + _Float16 *in = (qbyte*)(base+offset); for (i = 0; i < count; i++) { for (j = 0; j < e && j < sz; j++) out[i*e+j] = in[i*sz+j]; } } - break;*/ +#else + sz = 0; +#endif + break; case IQM_FLOAT: { float *in = (float*)(base+offset); @@ -6512,16 +6827,37 @@ static void IQM_ImportArrayF(qbyte *base, struct iqmvertexarray *src, float *out } } -galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer, size_t fsize) +const void *IQM_FindExtension(const char *buffer, const char *extname, int index, size_t *extsize) { struct iqmheader *h = (struct iqmheader *)buffer; - struct iqmmesh *mesh; - struct iqmvertexarray *varray; - struct iqmtriangle *tris; - struct iqmanim *anim; - unsigned int i, j, t, nt; + const char *strings = buffer + h->ofs_text; + struct iqmextension *ext; + int i; + for (i = 0, ext = (struct iqmextension*)(buffer + h->ofs_extensions); i < h->num_extensions; i++, ext = (struct iqmextension*)(buffer + ext->ofs_extensions)) + { + if (!Q_strcasecmp(strings + ext->name, extname) && index-->=0) + { + *extsize = ext->num_data; + return buffer + ext->ofs_data; + } + } + *extsize = 0; + return NULL; +} - char *strings; +galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, size_t fsize) +{ + const struct iqmheader *h = (struct iqmheader *)buffer; + const struct iqmmesh *mesh; + const struct iqmvertexarray *varray; + const struct iqmtriangle *tris; + const struct iqmanim *anim; + const struct iqmext_fte_mesh *ftemesh; + const struct iqmext_fte_event *fteevents; + const char *strings; + + unsigned int i, j, t, numtris, numverts, firstvert, firsttri; + size_t extsize; float *vtang = NULL; struct iqmvertexarray vpos = {0}, vnorm = {0}, vtcoord = {0}, vbone = {0}, vweight = {0}, vrgba = {0}; @@ -6557,6 +6893,7 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer, size_t fsize) qboolean noweights; frameinfo_t *framegroups; int numgroups; + qboolean fuckedevents = false; if (memcmp(h->magic, IQM_MAGIC, sizeof(h->magic))) { @@ -6599,11 +6936,11 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer, size_t fsize) Con_Printf("Unrecognised iqm info (type=%i, fmt=%i, size=%i)\n", type, fmt, size); } - if (!h->num_meshes) + /*if (!h->num_meshes) { Con_Printf("%s: IQM has no meshes\n", mod->name); return NULL; - } + }*/ //a mesh must contain vertex coords or its not much of a mesh. //we also require texcoords because we can. @@ -6615,7 +6952,9 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer, size_t fsize) return NULL; } noweights = !vbone.offset || !vweight.offset; - if (noweights) + if (!h->num_meshes) + noweights = true; + else if (noweights) { if (h->num_frames || h->num_anims || h->num_joints) { @@ -6629,6 +6968,26 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer, size_t fsize) Con_Printf("%s: IQM has %u joints, max supported is %u.\n", mod->name, h->num_joints, MAX_BONES); return NULL; } + if (h->num_poses > MAX_BONES) + { + Con_Printf("%s: IQM has %u bones, max supported is %u.\n", mod->name, h->num_joints, MAX_BONES); + return NULL; + } + if (h->num_joints && h->num_poses && h->num_joints != h->num_poses) + { + Con_Printf("%s: IQM has mismatched joints (%i vs %i).\n", mod->name, h->num_joints, h->num_poses); + return NULL; + } + if (h->num_meshes && !h->num_joints) + { + Con_Printf("%s: mesh IQM has no poses.\n", mod->name); + return NULL; + } + if (h->num_frames && !h->num_poses) + { + Con_Printf("%s: animated IQM has no poses.\n", mod->name); + return NULL; + } strings = buffer + h->ofs_text; @@ -6653,6 +7012,8 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer, size_t fsize) Q_strncpyz(framegroups[i].name, strings+anim[i].name, sizeof(fgroup[i].name)); } } + else + fuckedevents = true; //we're not using the animation data from the model, so ignore any events because they won't make sense any more. if (!numgroups) { /*base frame only*/ numgroups = 1; @@ -6680,7 +7041,7 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer, size_t fsize) if (i) obase = ZG_Malloc(&mod->memgroup, memsize); memsize = 0; - dalloc(gai, h->num_meshes); + dalloc(gai, max(1,h->num_meshes)); dalloc(bones, h->num_joints); dalloc(opos, h->num_vertexes); dalloc(onorm1, h->num_vertexes); @@ -6838,83 +7199,146 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer, size_t fsize) } free(framegroups); - //determine the bounds - inbounds = (struct iqmbounds*)(buffer + h->ofs_bounds); - for (i = 0; i < h->num_frames; i++) + if (fuckedevents) { - vec3_t mins, maxs; - mins[0] = LittleFloat(inbounds[i].bbmin[0]); - mins[1] = LittleFloat(inbounds[i].bbmin[1]); - mins[2] = LittleFloat(inbounds[i].bbmin[2]); - AddPointToBounds(mins, mod->mins, mod->maxs); - maxs[0] = LittleFloat(inbounds[i].bbmax[0]); - maxs[1] = LittleFloat(inbounds[i].bbmax[1]); - maxs[2] = LittleFloat(inbounds[i].bbmax[2]); - AddPointToBounds(maxs, mod->mins, mod->maxs); + fteevents = NULL; + extsize = 0; + } + else + fteevents = IQM_FindExtension(buffer, "FTE_EVENT", 0, &extsize); + if (fteevents && !(extsize % sizeof(*fteevents))) + { + galiasevent_t *oevent, **link; + extsize /= sizeof(*fteevents); + oevent = ZG_Malloc(&mod->memgroup, sizeof(*oevent)*extsize); + for (extsize /= sizeof(*fteevents); extsize>0; extsize--, fteevents++,oevent++) + { + oevent->timestamp = fteevents->timestamp; + oevent->code = fteevents->evcode; + oevent->data = ZG_Malloc(&mod->memgroup, strlen(strings+fteevents->evdata_str)+1); + strcpy(oevent->data, strings+fteevents->evdata_str); + oevent->timestamp /= fgroup[fteevents->anim].rate; + link = &fgroup[fteevents->anim].events; + while (*link && (*link)->timestamp <= oevent->timestamp) + link = &(*link)->next; + oevent->next = *link; + *link = oevent; + } } - for (i = 0; i < h->num_meshes; i++) + //determine the bounds + inbounds = (struct iqmbounds*)(buffer + h->ofs_bounds); + if (h->ofs_bounds) { - gai[i].nextsurf = (i == (h->num_meshes-1))?NULL:&gai[i+1]; + for (i = 0; i < h->num_frames; i++) + { + vec3_t mins, maxs; + mins[0] = LittleFloat(inbounds[i].bbmin[0]); + mins[1] = LittleFloat(inbounds[i].bbmin[1]); + mins[2] = LittleFloat(inbounds[i].bbmin[2]); + AddPointToBounds(mins, mod->mins, mod->maxs); + maxs[0] = LittleFloat(inbounds[i].bbmax[0]); + maxs[1] = LittleFloat(inbounds[i].bbmax[1]); + maxs[2] = LittleFloat(inbounds[i].bbmax[2]); + AddPointToBounds(maxs, mod->mins, mod->maxs); + } + } + else + AddPointToBounds(vec3_origin, mod->mins, mod->maxs); + + ftemesh = IQM_FindExtension(buffer, "FTE_MESH", 0, &extsize); + if (!extsize || extsize != sizeof(*ftemesh)*h->num_meshes) + ftemesh = NULL; //erk. + + for (i = 0; i < max(1, h->num_meshes); i++) + { + if (h->num_meshes) + { + Mod_DefaultMesh(&gai[i], strings+mesh[i].name, i); + firstvert = LittleLong(mesh[i].first_vertex); + numverts = LittleLong(mesh[i].num_vertexes); + numtris = LittleLong(mesh[i].num_triangles); + firsttri = LittleLong(mesh[i].first_triangle); + } + else + { //animation-only models still need a place-holder mesh. + firstvert = 0; + numverts = 0; + numtris = 0; + firsttri = 0; + } + + gai[i].nextsurf = &gai[i+1]; /*animation info*/ gai[i].shares_bones = 0; - gai[i].numbones = h->num_joints; + gai[i].numbones = h->num_joints?h->num_joints:h->num_poses; gai[i].ofsbones = bones; gai[i].numanimations = numgroups; gai[i].ofsanimations = fgroup; - - offset = LittleLong(mesh[i].first_vertex); - - Q_strncpyz(gai[i].surfacename, strings+mesh[i].name, sizeof(gai[i].surfacename)); #ifndef SERVERONLY /*colours*/ - gai[i].ofs_rgbaf = orgbaf?(orgbaf+offset):NULL; + gai[i].ofs_rgbaf = orgbaf?(orgbaf+firstvert):NULL; gai[i].ofs_rgbaub = NULL; /*texture coords*/ - gai[i].ofs_st_array = (otcoords+offset); + gai[i].ofs_st_array = (otcoords+firstvert); /*skins*/ - gai[i].numskins = skinfiles; - gai[i].ofsskins = skin; - - for (j = 0; j < skinfiles; j++) + if (h->num_meshes) { - skin->skinwidth = 1; - skin->skinheight = 1; - skin->skinspeed = 10; /*something to avoid div by 0*/ - skin->numframes = 1; //non-sequenced skins. - skin->frame = skinframe; - skin++; + gai[i].numskins = skinfiles; + gai[i].ofsskins = skin; - Q_strncpyz(skinframe->shadername, strings+mesh[i].material, sizeof(skinframe->shadername)); - skinframe++; + for (j = 0; j < skinfiles; j++) + { + skin->skinwidth = 1; + skin->skinheight = 1; + skin->skinspeed = 10; /*something to avoid div by 0*/ + skin->numframes = 1; //non-sequenced skins. + skin->frame = skinframe; + Q_strncpyz(skin->name, "", sizeof(skinframe->shadername)); + skin++; + + Q_strncpyz(skinframe->shadername, strings+mesh[i].material, sizeof(skinframe->shadername)); + skinframe++; + } } #endif - nt = LittleLong(mesh[i].num_triangles); tris = (struct iqmtriangle*)(buffer + LittleLong(h->ofs_triangles)); - tris += LittleLong(mesh[i].first_triangle); - gai[i].numindexes = nt*3; + tris += firsttri; + gai[i].numindexes = numtris*3; idx = ZG_Malloc(&mod->memgroup, sizeof(*idx)*gai[i].numindexes); gai[i].ofs_indexes = idx; - for (t = 0; t < nt; t++) + for (t = 0; t < numtris; t++) { - *idx++ = LittleLong(tris[t].vertex[0]) - offset; - *idx++ = LittleLong(tris[t].vertex[1]) - offset; - *idx++ = LittleLong(tris[t].vertex[2]) - offset; + *idx++ = LittleLong(tris[t].vertex[0]) - firstvert; + *idx++ = LittleLong(tris[t].vertex[1]) - firstvert; + *idx++ = LittleLong(tris[t].vertex[2]) - firstvert; } /*verts*/ gai[i].shares_verts = i; - gai[i].numverts = LittleLong(mesh[i].num_vertexes); - gai[i].ofs_skel_xyz = (opos+offset); - gai[i].ofs_skel_norm = (onorm1+offset); - gai[i].ofs_skel_svect = (onorm2+offset); - gai[i].ofs_skel_tvect = (onorm3+offset); - gai[i].ofs_skel_idx = oindex?(oindex+offset):NULL; - gai[i].ofs_skel_weight = oweight?(oweight+offset):NULL; + gai[i].numverts = numverts; + gai[i].ofs_skel_xyz = (opos+firstvert); + gai[i].ofs_skel_norm = (onorm1+firstvert); + gai[i].ofs_skel_svect = (onorm2+firstvert); + gai[i].ofs_skel_tvect = (onorm3+firstvert); + gai[i].ofs_skel_idx = oindex?(oindex+firstvert):NULL; + gai[i].ofs_skel_weight = oweight?(oweight+firstvert):NULL; + + if (ftemesh) + { //if we have extensions, then lets use them! + gai[i].geomset = ftemesh[i].geomset; + gai[i].geomid = ftemesh[i].geomid; + gai[i].contents = ftemesh[i].contents; + gai[i].csurface.flags = ftemesh[i].surfaceflags; + gai[i].surfaceid = ftemesh[i].surfaceid; + gai[i].mindist = ftemesh[i].mindist; + gai[i].maxdist = ftemesh[i].maxdist; + } } + gai[i-1].nextsurf = NULL; if (!noweights) { if (!IQM_ImportArray4B(buffer, &vbone, oindex, h->num_vertexes, h->num_joints)) @@ -6987,10 +7411,12 @@ qboolean QDECL Mod_LoadInterQuakeModel(model_t *mod, void *buffer, size_t fsize) mod->radius = RadiusFromBounds(mod->mins, mod->maxs); Mod_ClampModelSize(mod); + Mod_ParseModelEvents(mod, root->ofsanimations, root->numanimations); mod->numframes = root->numanimations; mod->meshinfo = root; mod->type = mod_alias; + mod->funcs.NativeTrace = Mod_Trace; return true; } @@ -7381,6 +7807,7 @@ galiasinfo_t *Mod_ParseMD5MeshModel(model_t *mod, char *buffer, char *modname) lastsurf->nextsurf = inf; lastsurf = inf; } + Mod_DefaultMesh(inf, COM_SkipPath(mod->name), 0); inf->ofsbones = bones; inf->numbones = numjoints; @@ -7586,7 +8013,7 @@ qboolean QDECL Mod_LoadMD5MeshModel(model_t *mod, void *buffer, size_t fsize) mod->flags = Mod_ReadFlagsFromMD1(mod->name, 0); //file replacement - inherit flags from any defunc mdl files. Mod_ClampModelSize(mod); - + Mod_ParseModelEvents(mod, root->ofsanimations, root->numanimations); mod->type = mod_alias; mod->numframes = root->numanimations; @@ -7765,6 +8192,7 @@ qboolean QDECL Mod_LoadCompositeAnim(model_t *mod, void *buffer, size_t fsize) mod->flags = Mod_ReadFlagsFromMD1(mod->name, 0); //file replacement - inherit flags from any defunc mdl files. Mod_ClampModelSize(mod); + Mod_ParseModelEvents(mod, root->ofsanimations, root->numanimations); mod->type = mod_alias; mod->numframes = root->numanimations; @@ -7779,11 +8207,13 @@ qboolean QDECL Mod_LoadCompositeAnim(model_t *mod, void *buffer, size_t fsize) void Alias_Register(void) { +#ifdef MD1MODELS Mod_RegisterModelFormatMagic(NULL, "Quake1 Model (mdl)", IDPOLYHEADER, Mod_LoadQ1Model); Mod_RegisterModelFormatMagic(NULL, "QuakeForge 16bit Model", (('6'<<24)+('1'<<16)+('D'<<8)+'M'), Mod_LoadQ1Model); #ifdef HEXEN2 Mod_RegisterModelFormatMagic(NULL, "Hexen2 Model (mdl)", RAPOLYHEADER, Mod_LoadQ1Model); #endif +#endif #ifdef MD2MODELS Mod_RegisterModelFormatMagic(NULL, "Quake2 Model (md2)", MD2IDALIASHEADER, Mod_LoadQ2Model); #endif diff --git a/engine/common/com_mesh.h b/engine/common/com_mesh.h index 1b90331cc..5653e9753 100644 --- a/engine/common/com_mesh.h +++ b/engine/common/com_mesh.h @@ -32,6 +32,14 @@ typedef struct vec3_t scale_origin; } galiaspose_t; +typedef struct galiasevent_s +{ + struct galiasevent_s *next; + float timestamp; + int code; + char *data; +} galiasevent_t; + //a frame group (aka: animation) typedef struct { @@ -44,6 +52,7 @@ typedef struct int numposes; float rate; galiaspose_t *poseofs; + galiasevent_t *events; char name[64]; } galiasanimation_t; @@ -120,6 +129,14 @@ typedef struct galiasinfo_s index_t *ofs_indexes; int numindexes; + //for hitmodel + unsigned int contents; //default CONTENTS_BODY + q2csurface_t csurface; //flags, and also collision name, if useful... + unsigned int surfaceid; //the body reported to qc via trace_surface + + float mindist; + float maxdist; + int *ofs_trineighbours; float lerpcutoff; //hack. should probably be part of the entity structure, but I really don't want new models (and thus code) to have access to this ugly inefficient hack. make your models properly in the first place. @@ -148,8 +165,6 @@ typedef struct galiasinfo_s float *baseframeofs; /*non-heirachical*/ int numbones; galiasbone_t *ofsbones; -//FTE_DEPRECATED int numswtransforms; -//FTE_DEPRECATED galisskeletaltransforms_t *ofsswtransforms; vecV_t *ofs_skel_xyz; vec3_t *ofs_skel_norm; diff --git a/engine/common/com_phys_bullet.cpp b/engine/common/com_phys_bullet.cpp index c732f448f..7361547ac 100644 --- a/engine/common/com_phys_bullet.cpp +++ b/engine/common/com_phys_bullet.cpp @@ -143,10 +143,10 @@ static void QDECL World_Bullet_End(world_t *world) struct bulletcontext_s *ctx = (struct bulletcontext_s*)world->rbe; world->rbe = NULL; delete ctx->dworld; - delete ctx->solver; - delete ctx->collisionconfig; - delete ctx->collisiondispatcher; - delete ctx->broadphase; + delete ctx->solver; + delete ctx->collisionconfig; + delete ctx->collisiondispatcher; + delete ctx->broadphase; delete ctx->ownerfilter; Z_Free(ctx); } @@ -819,24 +819,24 @@ public: trans.setOrigin(org); } QCMotionState(wedict_t *ed, world_t *w) - { + { dirty = qtrue; edict = ed; world = w; ReloadMotionState(); - } + } virtual ~QCMotionState() { } - virtual void getWorldTransform(btTransform &worldTrans) const - { + virtual void getWorldTransform(btTransform &worldTrans) const + { worldTrans = trans; - } + } - virtual void setWorldTransform(const btTransform &worldTrans) - { + virtual void setWorldTransform(const btTransform &worldTrans) + { vec3_t fwd, left, up, offset; trans = worldTrans; @@ -872,7 +872,7 @@ public: // mSceneNode ->setOrientation(rot.w(), rot.x(), rot.y(), rot.z()); // btVector3 pos = worldTrans.getOrigin(); // mSceneNode ->setPosition(pos.x(), pos.y(), pos.z()); - } + } }; static void World_Bullet_Frame_BodyFromEntity(world_t *world, wedict_t *ed) diff --git a/engine/common/fs.c b/engine/common/fs.c index d22b005ba..f4a66c260 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -766,7 +766,7 @@ static int QDECL COM_Dir_List(const char *name, qofs_t size, time_t mtime, void || !Q_strcasecmp(link, "ent") || !Q_strcasecmp(link, "rtlights") || !Q_strcasecmp(link, "shader") || !Q_strcasecmp(link, "framegroups")) Q_snprintfz(link, sizeof(link), "\\edit\\%s", name); - else if (!Q_strcasecmp(link, "tga") || !Q_strcasecmp(link, "png") || !Q_strcasecmp(link, "jpg") || !Q_strcasecmp(link, "jpeg") || !Q_strcasecmp(link, "lmp") || !Q_strcasecmp(link, "pcx")) + else if (!Q_strcasecmp(link, "tga") || !Q_strcasecmp(link, "png") || !Q_strcasecmp(link, "jpg") || !Q_strcasecmp(link, "jpeg") || !Q_strcasecmp(link, "lmp") || !Q_strcasecmp(link, "pcx")|| !Q_strcasecmp(link, "bmp")) { //FIXME: image replacements are getting in the way here. Q_snprintfz(link, sizeof(link), "\\tiprawimg\\%s\\tip\\(note: image replacement rules are context-dependant, including base path, sub path, extension, or complete replacement via a shader)", name); @@ -1255,6 +1255,9 @@ fail: return depth+1; } +//returns the package/'gamedir/foo.pk3' filename to tell the client to download +//unfortunately foo.pk3 may contain a 'bar.pk3' and downloading dir/foo.pk3/bar.pk3 won't work +//so if loc->search is dir/foo.pk3/bar.pk3 find dir/foo.pk3 instead char *FS_GetPackageDownloadFilename(flocation_t *loc) { searchpath_t *sp, *search; @@ -1263,11 +1266,11 @@ char *FS_GetPackageDownloadFilename(flocation_t *loc) { for (search = com_searchpaths; search; search = search->next) { - if (search != sp && search->handle->GeneratePureCRC) - { //only consider files that have a pure hash. this excludes system paths - if (!strncmp(search->purepath, sp->purepath, strlen(search->purepath))) - break; - } + if (search != sp) + if (search->handle->GeneratePureCRC) //only consider files that have a pure hash. this excludes system paths + if (!strncmp(search->purepath, sp->purepath, strlen(search->purepath))) + if (sp->purepath[strlen(search->purepath)] == '/') //also ensures that the path gets shorter, avoiding infinite loops as it fights between base+home dirs. + break; } if (search) sp = search; @@ -1275,7 +1278,7 @@ char *FS_GetPackageDownloadFilename(flocation_t *loc) break; } - if (sp && strchr(sp->purepath, '/')) + if (sp && strchr(sp->purepath, '/')) //never allow any packages that are directly sitting in the basedir. return sp->purepath; return NULL; } diff --git a/engine/common/gl_q2bsp.c b/engine/common/gl_q2bsp.c index be0f31d08..51a07c9c6 100644 --- a/engine/common/gl_q2bsp.c +++ b/engine/common/gl_q2bsp.c @@ -90,7 +90,7 @@ void CalcSurfaceExtents (model_t *mod, msurface_t *s) int idx; mins[0] = mins[1] = 999999; - maxs[0] = maxs[1] = -99999; + maxs[0] = maxs[1] = -999999; tex = s->texinfo; diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 718886cec..20062c0ce 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -608,9 +608,12 @@ void VARGS PR_CB_Free(void *mem) //DP_QC_GETSURFACE static void PF_BuildSurfaceMesh(model_t *model, unsigned int surfnum) { + //this function might be called on dedicated servers. +#ifdef Q1BSPS void ModQ1_Batches_BuildQ1Q2Poly(model_t *mod, msurface_t *surf, builddata_t *cookie); - if (model->fromgame == fg_quake) + if (model->fromgame == fg_quake || model->fromgame == fg_halflife) ModQ1_Batches_BuildQ1Q2Poly(model, &model->surfaces[surfnum], NULL); +#endif //fixme: q3... } // #434 float(entity e, float s) getsurfacenumpoints (DP_QC_GETSURFACE) @@ -5852,7 +5855,7 @@ void QCBUILTIN PF_physics_addtorque(pubprogfuncs_t *prinst, struct globalvars_s void PR_Common_Shutdown(pubprogfuncs_t *progs, qboolean errored) { #if defined(SKELETALOBJECTS) || defined(RAGDOLLS) - skel_reset(progs); + skel_reset(progs->parms->user); #endif PR_fclose_progs(progs); search_close_progs(progs, !errored); @@ -6221,6 +6224,8 @@ lh_extension_t QSG_Extensions[] = { {"FTE_FORCEINFOKEY", 1, NULL, {"forceinfokey"}, "Provides an easy way to change a user's userinfo from the server."}, {"FTE_GFX_QUAKE3SHADERS", 0, NULL, {NULL}, "specifies that the engine has full support for vanilla quake3 shaders"}, {"FTE_GFX_REMAPSHADER", 0, NULL, {NULL}, "With the raw power of stuffcmds, the r_remapshader console command is exposed! This mystical command can be used to remap any shader to another. Remapped shaders that specify $diffuse etc in some form will inherit the textures implied by the surface."}, +// {"FTE_GFX_IQM_HITMESH", 0, NULL, {NULL}, "Supports hitmesh iqm extensions. Also supports geomsets and embedded events."}, +// {"FTE_GFX_MODELEVENTS", 1, NULL, {"processmodelevents", "getnextmodelevent", "getmodeleventidx"}, "Provides a query for per-animation events in model files, including from progs/foo.mdl.events files."}, {"FTE_ISBACKBUFFERED", 1, NULL, {"isbackbuffered"}, "Allows you to check if a client has too many reliable messages pending."}, {"FTE_MEMALLOC", 4, NULL, {"memalloc", "memfree", "memcpy", "memfill8"}, "Allows dynamically allocating memory. Use pointers to access this memory. Memory will not be saved into saved games."}, #ifndef NOMEDIA diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h index a95294871..11a8fa112 100644 --- a/engine/common/pr_common.h +++ b/engine/common/pr_common.h @@ -262,10 +262,15 @@ void QCBUILTIN PF_setattachment(pubprogfuncs_t *prinst, struct globalvars_s *pr_ void QCBUILTIN PF_gettaginfo (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_gettagindex (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); #endif + +void QCBUILTIN PF_processmodelevents (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +void QCBUILTIN PF_getnextmodelevent (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +void QCBUILTIN PF_getmodeleventidx (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); + #if defined(SKELETALOBJECTS) || defined(RAGDOLL) - void skel_lookup(pubprogfuncs_t *prinst, int skelidx, framestate_t *out); - void skel_dodelete(pubprogfuncs_t *prinst); - void skel_reset(pubprogfuncs_t *prinst); + void skel_lookup(world_t *prinst, int skelidx, framestate_t *out); + void skel_dodelete(world_t *world); + void skel_reset(world_t *world); #endif void QCBUILTIN PF_physics_enable(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_physics_addforce(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); diff --git a/engine/common/q1bsp.c b/engine/common/q1bsp.c index e8c07eef9..2d2e97f0f 100644 --- a/engine/common/q1bsp.c +++ b/engine/common/q1bsp.c @@ -5,12 +5,646 @@ #include "shader.h" extern cvar_t r_decal_noperpendicular; + /* +Decal functions +*/ + +#define MAXFRAGMENTVERTS 360 +int Fragment_ClipPolyToPlane(float *inverts, float *outverts, int incount, float *plane, float planedist) +{ +#define C 4 + float dotv[MAXFRAGMENTVERTS+1]; + char keep[MAXFRAGMENTVERTS+1]; +#define KEEP_KILL 0 +#define KEEP_KEEP 1 +#define KEEP_BORDER 2 + int i; + int outcount = 0; + int clippedcount = 0; + float d, *p1, *p2, *out; +#define FRAG_EPSILON (1.0/32) //0.5 + + for (i = 0; i < incount; i++) + { + dotv[i] = DotProduct((inverts+i*C), plane) - planedist; + if (dotv[i]<-FRAG_EPSILON) + { + keep[i] = KEEP_KILL; + clippedcount++; + } + else if (dotv[i] > FRAG_EPSILON) + keep[i] = KEEP_KEEP; + else + keep[i] = KEEP_BORDER; + } + dotv[i] = dotv[0]; + keep[i] = keep[0]; + + if (clippedcount == incount) + return 0; //all were clipped + if (clippedcount == 0) + { //none were clipped + for (i = 0; i < incount; i++) + VectorCopy((inverts+i*C), (outverts+i*C)); + return incount; + } + + for (i = 0; i < incount; i++) + { + p1 = inverts+i*C; + if (keep[i] == KEEP_BORDER) + { + out = outverts+outcount++*C; + VectorCopy(p1, out); + continue; + } + if (keep[i] == KEEP_KEEP) + { + out = outverts+outcount++*C; + VectorCopy(p1, out); + } + if (keep[i+1] == KEEP_BORDER || keep[i] == keep[i+1]) + continue; + p2 = inverts+((i+1)%incount)*C; + d = dotv[i] - dotv[i+1]; + if (d) + d = dotv[i] / d; + + out = outverts+outcount++*C; + VectorInterpolate(p1, d, p2, out); + } + return outcount; +} + +//the plane itself must be a vec4_t, but can have other data packed between +size_t Fragment_ClipPlaneToBrush(vecV_t *points, size_t maxpoints, void *planes, size_t planestride, size_t numplanes, vec4_t face) +{ + int p, a; + vec4_t verts[MAXFRAGMENTVERTS]; + vec4_t verts2[MAXFRAGMENTVERTS]; + vec4_t *cverts; + int flip; +// vec3_t d1, d2, n; + size_t numverts; + + //generate some huge quad/poly aligned with the plane + vec3_t tmp; + vec3_t right, forward; + double t; + float *plane; + +// if (face[2] != 1) +// return 0; + + t = fabs(face[2]); + if (t > fabs(face[0]) && t > fabs(face[1])) + VectorSet(tmp, 1, 0, 0); + else + VectorSet(tmp, 0, 0, 1); + + CrossProduct(face, tmp, right); + VectorNormalize(right); + CrossProduct(face, right, forward); + VectorNormalize(forward); + + VectorScale(face, face[3], verts[0]); + VectorMA(verts[0], 32767, right, verts[0]); + VectorMA(verts[0], 32767, forward, verts[0]); + + VectorScale(face, face[3], verts[1]); + VectorMA(verts[1], 32767, right, verts[1]); + VectorMA(verts[1], -32767, forward, verts[1]); + + VectorScale(face, face[3], verts[2]); + VectorMA(verts[2], -32767, right, verts[2]); + VectorMA(verts[2], -32767, forward, verts[2]); + + VectorScale(face, face[3], verts[3]); + VectorMA(verts[3], -32767, right, verts[3]); + VectorMA(verts[3], 32767, forward, verts[3]); + + numverts = 4; + + + //clip the quad to the various other planes + flip = 0; + for (p = 0; p < numplanes; p++) + { + plane = (float*)((qbyte*)planes + p*planestride); + if (plane != face) + { + vec3_t norm; + flip^=1; + VectorNegate(plane, norm); + if (flip) + numverts = Fragment_ClipPolyToPlane((float*)verts, (float*)verts2, numverts, norm, -plane[3]); + else + numverts = Fragment_ClipPolyToPlane((float*)verts2, (float*)verts, numverts, norm, -plane[3]); + + if (numverts < 3) //totally clipped. + return 0; + } + } + + if (numverts > maxpoints) + return 0; + + if (flip) + cverts = verts2; + else + cverts = verts; + for (p = 0; p < numverts; p++) + { + for (a = 0; a < 3; a++) + { + float f = cverts[p][a]; + int rounded = floor(f + 0.5); + //if its within 1/1000th of a qu, just round it. + if (fabs(f - rounded) < 0.001) + points[p][a] = rounded; + else + points[p][a] = f; + } + } + + return numverts; +} + +#ifndef SERVERONLY + +#define MAXFRAGMENTTRIS 256 +vec3_t decalfragmentverts[MAXFRAGMENTTRIS*3]; + +struct fragmentdecal_s +{ + vec3_t center; + + vec3_t normal; +// vec3_t tangent1; +// vec3_t tangent2; + + vec3_t planenorm[6]; + float planedist[6]; + int numplanes; + + vec_t radius; + + //will only appear on surfaces with the matching surfaceflag + unsigned int surfflagmask; + unsigned int surfflagmatch; + + void (*callback)(void *ctx, vec3_t *fte_restrict points, size_t numpoints, shader_t *shader); + void *ctx; +}; + +//#define SHOWCLIPS +//#define FRAGMENTASTRIANGLES //works, but produces more fragments. + +#ifdef FRAGMENTASTRIANGLES + +//if the triangle is clipped away, go recursive if there are tris left. +static void Fragment_ClipTriToPlane(int trinum, float *plane, float planedist, fragmentdecal_t *dec) +{ + float *point[3]; + float dotv[3]; + + vec3_t impact1, impact2; + float t; + + int i, i2, i3; + int clippedverts = 0; + + for (i = 0; i < 3; i++) + { + point[i] = decalfragmentverts[trinum*3+i]; + dotv[i] = DotProduct(point[i], plane)-planedist; + clippedverts += dotv[i] < 0; + } + + //if they're all clipped away, scrap the tri + switch (clippedverts) + { + case 0: + return; //plane does not clip the triangle. + + case 1: //split into 3, disregard the clipped vert + for (i = 0; i < 3; i++) + { + if (dotv[i] < 0) + { //This is the vertex that's getting clipped. + + if (dotv[i] > -DIST_EPSILON) + return; //it's only over the line by a tiny ammount. + + i2 = (i+1)%3; + i3 = (i+2)%3; + + if (dotv[i2] < DIST_EPSILON) + return; + if (dotv[i3] < DIST_EPSILON) + return; + + //work out where the two lines impact the plane + t = (dotv[i]) / (dotv[i]-dotv[i2]); + VectorInterpolate(point[i], t, point[i2], impact1); + + t = (dotv[i]) / (dotv[i]-dotv[i3]); + VectorInterpolate(point[i], t, point[i3], impact2); + +#ifdef SHOWCLIPS + if (dec->numtris != MAXFRAGMENTTRIS) + { + VectorCopy(impact2, decalfragmentverts[dec->numtris*3+0]); + VectorCopy(decalfragmentverts[trinum*3+i], decalfragmentverts[dec->numtris*3+1]); + VectorCopy(impact1, decalfragmentverts[dec->numtris*3+2]); + dec->numtris++; + } +#endif + + + //shrink the tri, putting the impact into the killed vertex. + VectorCopy(impact2, point[i]); + + + if (dec->numtris == MAXFRAGMENTTRIS) + return; //:( + + //build the second tri + VectorCopy(impact1, decalfragmentverts[dec->numtris*3+0]); + VectorCopy(decalfragmentverts[trinum*3+i2], decalfragmentverts[dec->numtris*3+1]); + VectorCopy(impact2, decalfragmentverts[dec->numtris*3+2]); + dec->numtris++; + + return; + } + } + Sys_Error("Fragment_ClipTriToPlane: Clipped vertex not founc\n"); + return; //can't handle it + case 2: //split into 3, disregarding both the clipped. + for (i = 0; i < 3; i++) + { + if (!(dotv[i] < 0)) + { //This is the vertex that's staying. + + if (dotv[i] < DIST_EPSILON) + break; //only just inside + + i2 = (i+1)%3; + i3 = (i+2)%3; + + //work out where the two lines impact the plane + t = (dotv[i]) / (dotv[i]-dotv[i2]); + VectorInterpolate(point[i], t, point[i2], impact1); + + t = (dotv[i]) / (dotv[i]-dotv[i3]); + VectorInterpolate(point[i], t, point[i3], impact2); + + //shrink the tri, putting the impact into the killed vertex. + +#ifdef SHOWCLIPS + if (dec->numtris != MAXFRAGMENTTRIS) + { + VectorCopy(impact1, decalfragmentverts[dec->numtris*3+0]); + VectorCopy(point[i2], decalfragmentverts[dec->numtris*3+1]); + VectorCopy(point[i3], decalfragmentverts[dec->numtris*3+2]); + dec->numtris++; + } + if (dec->numtris != MAXFRAGMENTTRIS) + { + VectorCopy(impact1, decalfragmentverts[dec->numtris*3+0]); + VectorCopy(point[i3], decalfragmentverts[dec->numtris*3+1]); + VectorCopy(impact2, decalfragmentverts[dec->numtris*3+2]); + dec->numtris++; + } +#endif + + VectorCopy(impact1, point[i2]); + VectorCopy(impact2, point[i3]); + return; + } + } + case 3://scrap it + //fill the verts with the verts of the last and go recursive (due to the nature of Fragment_ClipTriangle, which doesn't actually know if we clip them away) +#ifndef SHOWCLIPS + dec->numtris--; + VectorCopy(decalfragmentverts[dec->numtris*3+0], decalfragmentverts[trinum*3+0]); + VectorCopy(decalfragmentverts[dec->numtris*3+1], decalfragmentverts[trinum*3+1]); + VectorCopy(decalfragmentverts[dec->numtris*3+2], decalfragmentverts[trinum*3+2]); + if (trinum < dec->numtris) + Fragment_ClipTriToPlane(trinum, plane, planedist, dec); +#endif + return; + } +} + +static void Fragment_ClipTriangle(fragmentdecal_t *dec, float *a, float *b, float *c) +{ + //emit the triangle, and clip it's fragments. + int start, i; + + int p; + + if (dec->numtris == MAXFRAGMENTTRIS) + return; //:( + + start = dec->numtris; + + VectorCopy(a, decalfragmentverts[dec->numtris*3+0]); + VectorCopy(b, decalfragmentverts[dec->numtris*3+1]); + VectorCopy(c, decalfragmentverts[dec->numtris*3+2]); + dec->numtris++; + + //clip all the fragments to all of the planes. + //This will produce a quad if the source triangle was big enough. + + for (p = 0; p < 6; p++) + { + for (i = start; i < dec->numtris; i++) + Fragment_ClipTriToPlane(i, dec->planenorm[p], dec->plantdist[p], dec); + } +} + +#else + +void Fragment_ClipPoly(fragmentdecal_t *dec, int numverts, float *inverts, shader_t *surfshader) +{ + //emit the triangle, and clip it's fragments. + int p; + float verts[MAXFRAGMENTVERTS*C]; + float verts2[MAXFRAGMENTVERTS*C]; + float *cverts; + int flip; + vec3_t d1, d2, n; + size_t numtris; + + if (numverts > MAXFRAGMENTTRIS) + return; + + if (r_decal_noperpendicular.ival) + { + VectorSubtract(inverts+C*1, inverts+C*0, d1); + for (p = 2; ; p++) + { + if (p >= numverts) + return; + VectorSubtract(inverts+C*p, inverts+C*0, d2); + CrossProduct(d1, d2, n); + if (DotProduct(n,n)>.1) + break; + } + VectorNormalizeFast(n); + if (DotProduct(n, dec->normal) < 0.1) + return; //faces too far way from the normal + } + + //clip to the first plane specially, so we don't have extra copys + numverts = Fragment_ClipPolyToPlane(inverts, verts, numverts, dec->planenorm[0], dec->planedist[0]); + + //clip the triangle to the 6 planes. + flip = 0; + for (p = 1; p < dec->numplanes; p++) + { + flip^=1; + if (flip) + numverts = Fragment_ClipPolyToPlane(verts, verts2, numverts, dec->planenorm[p], dec->planedist[p]); + else + numverts = Fragment_ClipPolyToPlane(verts2, verts, numverts, dec->planenorm[p], dec->planedist[p]); + + if (numverts < 3) //totally clipped. + return; + } + + if (flip) + cverts = verts2; + else + cverts = verts; + + //decompose the resultant polygon into triangles. + + numtris = 0; + while(numverts-->2) + { + if (numtris == MAXFRAGMENTTRIS) + { + dec->callback(dec->ctx, decalfragmentverts, numtris, NULL); + numtris = 0; + break; + } + + VectorCopy((cverts+C*0), decalfragmentverts[numtris*3+0]); + VectorCopy((cverts+C*(numverts-1)), decalfragmentverts[numtris*3+1]); + VectorCopy((cverts+C*numverts), decalfragmentverts[numtris*3+2]); + numtris++; + } + if (numtris) + dec->callback(dec->ctx, decalfragmentverts, numtris, surfshader); +} + +#endif + +//this could be inlined, but I'm lazy. +static void Fragment_Mesh (fragmentdecal_t *dec, mesh_t *mesh, mtexinfo_t *texinfo) +{ + int i; + vecV_t verts[3]; + shader_t *surfshader = texinfo->texture->shader; + + if (surfshader->flags & SHADER_NOMARKS) + return; + + if (dec->surfflagmask) + { + if ((texinfo->flags & dec->surfflagmask) != dec->surfflagmatch) + return; + } + + /*if its a triangle fan/poly/quad then we can just submit the entire thing without generating extra fragments*/ + if (mesh->istrifan) + { + Fragment_ClipPoly(dec, mesh->numvertexes, mesh->xyz_array[0], surfshader); + return; + } + + //Fixme: optimise q3 patches (quad strips with bends between each strip) + + /*otherwise it goes in and out in weird places*/ + for (i = 0; i < mesh->numindexes; i+=3) + { + VectorCopy(mesh->xyz_array[mesh->indexes[i+0]], verts[0]); + VectorCopy(mesh->xyz_array[mesh->indexes[i+1]], verts[1]); + VectorCopy(mesh->xyz_array[mesh->indexes[i+2]], verts[2]); + Fragment_ClipPoly(dec, 3, verts[0], surfshader); + } +} + +#ifdef Q1BSPS +static void Q1BSP_ClipDecalToNodes (model_t *mod, fragmentdecal_t *dec, mnode_t *node) +{ + mplane_t *splitplane; + float dist; + msurface_t *surf; + int i; + + if (node->contents < 0) + return; + + splitplane = node->plane; + dist = DotProduct (dec->center, splitplane->normal) - splitplane->dist; + + if (dist > dec->radius) + { + Q1BSP_ClipDecalToNodes (mod, dec, node->children[0]); + return; + } + if (dist < -dec->radius) + { + Q1BSP_ClipDecalToNodes (mod, dec, node->children[1]); + return; + } + +// mark the polygons + surf = mod->surfaces + node->firstsurface; + if (r_decal_noperpendicular.ival) + { + for (i=0 ; inumsurfaces ; i++, surf++) + { + if (surf->flags & SURF_PLANEBACK) + { + if (-DotProduct(surf->plane->normal, dec->normal) > -0.5) + continue; + } + else + { + if (DotProduct(surf->plane->normal, dec->normal) > -0.5) + continue; + } + Fragment_Mesh(dec, surf->mesh, surf->texinfo); + } + } + else + { + for (i=0 ; inumsurfaces ; i++, surf++) + Fragment_Mesh(dec, surf->mesh, surf->texinfo); + } + + Q1BSP_ClipDecalToNodes (mod, dec, node->children[0]); + Q1BSP_ClipDecalToNodes (mod, dec, node->children[1]); +} +#endif + +#ifdef RTLIGHTS +extern int sh_shadowframe; +#else +static int sh_shadowframe; +#endif +#ifdef Q3BSPS +static void Q3BSP_ClipDecalToNodes (fragmentdecal_t *dec, mnode_t *node) +{ + mplane_t *splitplane; + float dist; + msurface_t **msurf; + msurface_t *surf; + mleaf_t *leaf; + int i; + + if (node->contents != -1) + { + leaf = (mleaf_t *)node; + // mark the polygons + msurf = leaf->firstmarksurface; + for (i=0 ; inummarksurfaces ; i++, msurf++) + { + surf = *msurf; + + //only check each surface once. it can appear in multiple leafs. + if (surf->shadowframe == sh_shadowframe) + continue; + surf->shadowframe = sh_shadowframe; + + Fragment_Mesh(dec, surf->mesh, surf->texinfo); + } + return; + } + + splitplane = node->plane; + dist = DotProduct (dec->center, splitplane->normal) - splitplane->dist; + + if (dist > dec->radius) + { + Q3BSP_ClipDecalToNodes (dec, node->children[0]); + return; + } + if (dist < -dec->radius) + { + Q3BSP_ClipDecalToNodes (dec, node->children[1]); + return; + } + Q3BSP_ClipDecalToNodes (dec, node->children[0]); + Q3BSP_ClipDecalToNodes (dec, node->children[1]); +} +#endif + +void Mod_ClipDecal(struct model_s *mod, vec3_t center, vec3_t normal, vec3_t tangent1, vec3_t tangent2, float size, unsigned int surfflagmask, unsigned int surfflagmatch, void (*callback)(void *ctx, vec3_t *fte_restrict points, size_t numpoints, shader_t *shader), void *ctx) +{ //quad marks a full, independant quad + int p; + float r; + fragmentdecal_t dec; + + VectorCopy(center, dec.center); + VectorCopy(normal, dec.normal); + dec.radius = 0; + dec.callback = callback; + dec.ctx = ctx; + dec.surfflagmask = surfflagmask; + dec.surfflagmatch = surfflagmatch; + + VectorCopy(tangent1, dec.planenorm[0]); + VectorNegate(tangent1, dec.planenorm[1]); + VectorCopy(tangent2, dec.planenorm[2]); + VectorNegate(tangent2, dec.planenorm[3]); + VectorCopy(dec.normal, dec.planenorm[4]); + VectorNegate(dec.normal, dec.planenorm[5]); + for (p = 0; p < 6; p++) + { + r = sqrt(DotProduct(dec.planenorm[p], dec.planenorm[p])); + VectorScale(dec.planenorm[p], 1/r, dec.planenorm[p]); + r*= size/2; + if (r > dec.radius) + dec.radius = r; + dec.planedist[p] = -(r - DotProduct(dec.center, dec.planenorm[p])); + } + dec.numplanes = 6; + + sh_shadowframe++; + + if (!mod || mod->loadstate != MLS_LOADED || mod->type != mod_brush) + { + } +#ifdef Q1BSPS + else if (mod->fromgame == fg_quake) + Q1BSP_ClipDecalToNodes(mod, &dec, mod->rootnode); +#endif +#ifdef Q3BSPS + else if (cl.worldmodel->fromgame == fg_quake3) + Q3BSP_ClipDecalToNodes(&dec, mod->rootnode); +#endif + +#ifdef TERRAIN + if (cl.worldmodel && cl.worldmodel->terrain) + Terrain_ClipDecal(&dec, center, dec.radius, mod); +#endif +} +#endif + +/* +Decal functions ============================================================================ Physics functions (common) */ +#ifdef Q1BSPS void Q1BSP_CheckHullNodes(hull_t *hull) { @@ -27,6 +661,24 @@ void Q1BSP_CheckHullNodes(hull_t *hull) } } + +static int Q1_ModelPointContents (mnode_t *node, vec3_t p) +{ + float d; + mplane_t *plane; + while(node->contents >= 0) + { + plane = node->plane; + if (plane->type < 3) + d = p[plane->type] - plane->dist; + else + d = DotProduct(plane->normal, p) - plane->dist; + node = node->children[d<0]; + } + return node->contents; +} + +#endif//Q1BSPS /* ================== SV_HullPointContents @@ -57,24 +709,6 @@ static int Q1_HullPointContents (hull_t *hull, int num, vec3_t p) return num; } -static int Q1_ModelPointContents (mnode_t *node, vec3_t p) -{ - float d; - mplane_t *plane; - while(node->contents >= 0) - { - plane = node->plane; - if (plane->type < 3) - d = p[plane->type] - plane->dist; - else - d = DotProduct(plane->normal, p) - plane->dist; - node = node->children[d<0]; - } - return node->contents; -} - - - #define DIST_EPSILON (0.03125) #if 1 enum @@ -374,6 +1008,7 @@ qboolean Q1BSP_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, } #endif +#ifdef Q1BSPS /* the bsp tree we're walking through is the renderable hull @@ -649,6 +1284,7 @@ static void Q1BSP_RecursiveBrushCheck (struct traceinfo_s *traceinfo, mnode_t *n // go past the node Q1BSP_RecursiveBrushCheck (traceinfo, node->children[side^1], midf, p2f, mid, p2); } +#endif //Q1BSPS static unsigned int Q1BSP_TranslateContents(int contents) { @@ -698,6 +1334,7 @@ int Q1BSP_HullPointContents(hull_t *hull, vec3_t p) return Q1BSP_TranslateContents(Q1_HullPointContents(hull, hull->firstclipnode, p)); } +#ifdef Q1BSPS unsigned int Q1BSP_PointContents(model_t *model, vec3_t axis[3], vec3_t point) { int contents; @@ -1021,173 +1658,6 @@ Physics functions (common) ============================================================================ -Utility function -*/ - -#define MAXFRAGMENTVERTS 360 -int Fragment_ClipPolyToPlane(float *inverts, float *outverts, int incount, float *plane, float planedist) -{ -#define C 4 - float dotv[MAXFRAGMENTVERTS+1]; - char keep[MAXFRAGMENTVERTS+1]; -#define KEEP_KILL 0 -#define KEEP_KEEP 1 -#define KEEP_BORDER 2 - int i; - int outcount = 0; - int clippedcount = 0; - float d, *p1, *p2, *out; -#define FRAG_EPSILON (1.0/32) //0.5 - - for (i = 0; i < incount; i++) - { - dotv[i] = DotProduct((inverts+i*C), plane) - planedist; - if (dotv[i]<-FRAG_EPSILON) - { - keep[i] = KEEP_KILL; - clippedcount++; - } - else if (dotv[i] > FRAG_EPSILON) - keep[i] = KEEP_KEEP; - else - keep[i] = KEEP_BORDER; - } - dotv[i] = dotv[0]; - keep[i] = keep[0]; - - if (clippedcount == incount) - return 0; //all were clipped - if (clippedcount == 0) - { //none were clipped - for (i = 0; i < incount; i++) - VectorCopy((inverts+i*C), (outverts+i*C)); - return incount; - } - - for (i = 0; i < incount; i++) - { - p1 = inverts+i*C; - if (keep[i] == KEEP_BORDER) - { - out = outverts+outcount++*C; - VectorCopy(p1, out); - continue; - } - if (keep[i] == KEEP_KEEP) - { - out = outverts+outcount++*C; - VectorCopy(p1, out); - } - if (keep[i+1] == KEEP_BORDER || keep[i] == keep[i+1]) - continue; - p2 = inverts+((i+1)%incount)*C; - d = dotv[i] - dotv[i+1]; - if (d) - d = dotv[i] / d; - - out = outverts+outcount++*C; - VectorInterpolate(p1, d, p2, out); - } - return outcount; -} - -//the plane itself must be a vec4_t, but can have other data packed between -size_t Fragment_ClipPlaneToBrush(vecV_t *points, size_t maxpoints, void *planes, size_t planestride, size_t numplanes, vec4_t face) -{ - int p, a; - vec4_t verts[MAXFRAGMENTVERTS]; - vec4_t verts2[MAXFRAGMENTVERTS]; - vec4_t *cverts; - int flip; -// vec3_t d1, d2, n; - size_t numverts; - - //generate some huge quad/poly aligned with the plane - vec3_t tmp; - vec3_t right, forward; - double t; - float *plane; - -// if (face[2] != 1) -// return 0; - - t = fabs(face[2]); - if (t > fabs(face[0]) && t > fabs(face[1])) - VectorSet(tmp, 1, 0, 0); - else - VectorSet(tmp, 0, 0, 1); - - CrossProduct(face, tmp, right); - VectorNormalize(right); - CrossProduct(face, right, forward); - VectorNormalize(forward); - - VectorScale(face, face[3], verts[0]); - VectorMA(verts[0], 32767, right, verts[0]); - VectorMA(verts[0], 32767, forward, verts[0]); - - VectorScale(face, face[3], verts[1]); - VectorMA(verts[1], 32767, right, verts[1]); - VectorMA(verts[1], -32767, forward, verts[1]); - - VectorScale(face, face[3], verts[2]); - VectorMA(verts[2], -32767, right, verts[2]); - VectorMA(verts[2], -32767, forward, verts[2]); - - VectorScale(face, face[3], verts[3]); - VectorMA(verts[3], -32767, right, verts[3]); - VectorMA(verts[3], 32767, forward, verts[3]); - - numverts = 4; - - - //clip the quad to the various other planes - flip = 0; - for (p = 0; p < numplanes; p++) - { - plane = (float*)((qbyte*)planes + p*planestride); - if (plane != face) - { - vec3_t norm; - flip^=1; - VectorNegate(plane, norm); - if (flip) - numverts = Fragment_ClipPolyToPlane((float*)verts, (float*)verts2, numverts, norm, -plane[3]); - else - numverts = Fragment_ClipPolyToPlane((float*)verts2, (float*)verts, numverts, norm, -plane[3]); - - if (numverts < 3) //totally clipped. - return 0; - } - } - - if (numverts > maxpoints) - return 0; - - if (flip) - cverts = verts2; - else - cverts = verts; - for (p = 0; p < numverts; p++) - { - for (a = 0; a < 3; a++) - { - float f = cverts[p][a]; - int rounded = floor(f + 0.5); - //if its within 1/1000th of a qu, just round it. - if (fabs(f - rounded) < 0.001) - points[p][a] = rounded; - else - points[p][a] = f; - } - } - - return numverts; -} - -/* -======================== - Rendering functions (Client only) */ #ifndef SERVERONLY @@ -1260,465 +1730,6 @@ void Q1BSP_MarkLights (dlight_t *light, int bit, mnode_t *node) Q1BSP_MarkLights (light, bit, node->children[1]); } -#define MAXFRAGMENTTRIS 256 -vec3_t decalfragmentverts[MAXFRAGMENTTRIS*3]; - -struct fragmentdecal_s -{ - vec3_t center; - - vec3_t normal; -// vec3_t tangent1; -// vec3_t tangent2; - - vec3_t planenorm[6]; - float planedist[6]; - int numplanes; - - vec_t radius; - - //will only appear on surfaces with the matching surfaceflag - unsigned int surfflagmask; - unsigned int surfflagmatch; - - void (*callback)(void *ctx, vec3_t *fte_restrict points, size_t numpoints, shader_t *shader); - void *ctx; -}; - -//#define SHOWCLIPS -//#define FRAGMENTASTRIANGLES //works, but produces more fragments. - -#ifdef FRAGMENTASTRIANGLES - -//if the triangle is clipped away, go recursive if there are tris left. -static void Fragment_ClipTriToPlane(int trinum, float *plane, float planedist, fragmentdecal_t *dec) -{ - float *point[3]; - float dotv[3]; - - vec3_t impact1, impact2; - float t; - - int i, i2, i3; - int clippedverts = 0; - - for (i = 0; i < 3; i++) - { - point[i] = decalfragmentverts[trinum*3+i]; - dotv[i] = DotProduct(point[i], plane)-planedist; - clippedverts += dotv[i] < 0; - } - - //if they're all clipped away, scrap the tri - switch (clippedverts) - { - case 0: - return; //plane does not clip the triangle. - - case 1: //split into 3, disregard the clipped vert - for (i = 0; i < 3; i++) - { - if (dotv[i] < 0) - { //This is the vertex that's getting clipped. - - if (dotv[i] > -DIST_EPSILON) - return; //it's only over the line by a tiny ammount. - - i2 = (i+1)%3; - i3 = (i+2)%3; - - if (dotv[i2] < DIST_EPSILON) - return; - if (dotv[i3] < DIST_EPSILON) - return; - - //work out where the two lines impact the plane - t = (dotv[i]) / (dotv[i]-dotv[i2]); - VectorInterpolate(point[i], t, point[i2], impact1); - - t = (dotv[i]) / (dotv[i]-dotv[i3]); - VectorInterpolate(point[i], t, point[i3], impact2); - -#ifdef SHOWCLIPS - if (dec->numtris != MAXFRAGMENTTRIS) - { - VectorCopy(impact2, decalfragmentverts[dec->numtris*3+0]); - VectorCopy(decalfragmentverts[trinum*3+i], decalfragmentverts[dec->numtris*3+1]); - VectorCopy(impact1, decalfragmentverts[dec->numtris*3+2]); - dec->numtris++; - } -#endif - - - //shrink the tri, putting the impact into the killed vertex. - VectorCopy(impact2, point[i]); - - - if (dec->numtris == MAXFRAGMENTTRIS) - return; //:( - - //build the second tri - VectorCopy(impact1, decalfragmentverts[dec->numtris*3+0]); - VectorCopy(decalfragmentverts[trinum*3+i2], decalfragmentverts[dec->numtris*3+1]); - VectorCopy(impact2, decalfragmentverts[dec->numtris*3+2]); - dec->numtris++; - - return; - } - } - Sys_Error("Fragment_ClipTriToPlane: Clipped vertex not founc\n"); - return; //can't handle it - case 2: //split into 3, disregarding both the clipped. - for (i = 0; i < 3; i++) - { - if (!(dotv[i] < 0)) - { //This is the vertex that's staying. - - if (dotv[i] < DIST_EPSILON) - break; //only just inside - - i2 = (i+1)%3; - i3 = (i+2)%3; - - //work out where the two lines impact the plane - t = (dotv[i]) / (dotv[i]-dotv[i2]); - VectorInterpolate(point[i], t, point[i2], impact1); - - t = (dotv[i]) / (dotv[i]-dotv[i3]); - VectorInterpolate(point[i], t, point[i3], impact2); - - //shrink the tri, putting the impact into the killed vertex. - -#ifdef SHOWCLIPS - if (dec->numtris != MAXFRAGMENTTRIS) - { - VectorCopy(impact1, decalfragmentverts[dec->numtris*3+0]); - VectorCopy(point[i2], decalfragmentverts[dec->numtris*3+1]); - VectorCopy(point[i3], decalfragmentverts[dec->numtris*3+2]); - dec->numtris++; - } - if (dec->numtris != MAXFRAGMENTTRIS) - { - VectorCopy(impact1, decalfragmentverts[dec->numtris*3+0]); - VectorCopy(point[i3], decalfragmentverts[dec->numtris*3+1]); - VectorCopy(impact2, decalfragmentverts[dec->numtris*3+2]); - dec->numtris++; - } -#endif - - VectorCopy(impact1, point[i2]); - VectorCopy(impact2, point[i3]); - return; - } - } - case 3://scrap it - //fill the verts with the verts of the last and go recursive (due to the nature of Fragment_ClipTriangle, which doesn't actually know if we clip them away) -#ifndef SHOWCLIPS - dec->numtris--; - VectorCopy(decalfragmentverts[dec->numtris*3+0], decalfragmentverts[trinum*3+0]); - VectorCopy(decalfragmentverts[dec->numtris*3+1], decalfragmentverts[trinum*3+1]); - VectorCopy(decalfragmentverts[dec->numtris*3+2], decalfragmentverts[trinum*3+2]); - if (trinum < dec->numtris) - Fragment_ClipTriToPlane(trinum, plane, planedist, dec); -#endif - return; - } -} - -static void Fragment_ClipTriangle(fragmentdecal_t *dec, float *a, float *b, float *c) -{ - //emit the triangle, and clip it's fragments. - int start, i; - - int p; - - if (dec->numtris == MAXFRAGMENTTRIS) - return; //:( - - start = dec->numtris; - - VectorCopy(a, decalfragmentverts[dec->numtris*3+0]); - VectorCopy(b, decalfragmentverts[dec->numtris*3+1]); - VectorCopy(c, decalfragmentverts[dec->numtris*3+2]); - dec->numtris++; - - //clip all the fragments to all of the planes. - //This will produce a quad if the source triangle was big enough. - - for (p = 0; p < 6; p++) - { - for (i = start; i < dec->numtris; i++) - Fragment_ClipTriToPlane(i, dec->planenorm[p], dec->plantdist[p], dec); - } -} - -#else - -void Fragment_ClipPoly(fragmentdecal_t *dec, int numverts, float *inverts, shader_t *surfshader) -{ - //emit the triangle, and clip it's fragments. - int p; - float verts[MAXFRAGMENTVERTS*C]; - float verts2[MAXFRAGMENTVERTS*C]; - float *cverts; - int flip; - vec3_t d1, d2, n; - size_t numtris; - - if (numverts > MAXFRAGMENTTRIS) - return; - - if (r_decal_noperpendicular.ival) - { - VectorSubtract(inverts+C*1, inverts+C*0, d1); - for (p = 2; ; p++) - { - if (p >= numverts) - return; - VectorSubtract(inverts+C*p, inverts+C*0, d2); - CrossProduct(d1, d2, n); - if (DotProduct(n,n)>.1) - break; - } - VectorNormalizeFast(n); - if (DotProduct(n, dec->normal) < 0.1) - return; //faces too far way from the normal - } - - //clip to the first plane specially, so we don't have extra copys - numverts = Fragment_ClipPolyToPlane(inverts, verts, numverts, dec->planenorm[0], dec->planedist[0]); - - //clip the triangle to the 6 planes. - flip = 0; - for (p = 1; p < dec->numplanes; p++) - { - flip^=1; - if (flip) - numverts = Fragment_ClipPolyToPlane(verts, verts2, numverts, dec->planenorm[p], dec->planedist[p]); - else - numverts = Fragment_ClipPolyToPlane(verts2, verts, numverts, dec->planenorm[p], dec->planedist[p]); - - if (numverts < 3) //totally clipped. - return; - } - - if (flip) - cverts = verts2; - else - cverts = verts; - - //decompose the resultant polygon into triangles. - - numtris = 0; - while(numverts-->2) - { - if (numtris == MAXFRAGMENTTRIS) - { - dec->callback(dec->ctx, decalfragmentverts, numtris, NULL); - numtris = 0; - break; - } - - VectorCopy((cverts+C*0), decalfragmentverts[numtris*3+0]); - VectorCopy((cverts+C*(numverts-1)), decalfragmentverts[numtris*3+1]); - VectorCopy((cverts+C*numverts), decalfragmentverts[numtris*3+2]); - numtris++; - } - if (numtris) - dec->callback(dec->ctx, decalfragmentverts, numtris, surfshader); -} - -#endif - -//this could be inlined, but I'm lazy. -static void Fragment_Mesh (fragmentdecal_t *dec, mesh_t *mesh, mtexinfo_t *texinfo) -{ - int i; - vecV_t verts[3]; - shader_t *surfshader = texinfo->texture->shader; - - if (surfshader->flags & SHADER_NOMARKS) - return; - - if (dec->surfflagmask) - { - if ((texinfo->flags & dec->surfflagmask) != dec->surfflagmatch) - return; - } - - /*if its a triangle fan/poly/quad then we can just submit the entire thing without generating extra fragments*/ - if (mesh->istrifan) - { - Fragment_ClipPoly(dec, mesh->numvertexes, mesh->xyz_array[0], surfshader); - return; - } - - //Fixme: optimise q3 patches (quad strips with bends between each strip) - - /*otherwise it goes in and out in weird places*/ - for (i = 0; i < mesh->numindexes; i+=3) - { - VectorCopy(mesh->xyz_array[mesh->indexes[i+0]], verts[0]); - VectorCopy(mesh->xyz_array[mesh->indexes[i+1]], verts[1]); - VectorCopy(mesh->xyz_array[mesh->indexes[i+2]], verts[2]); - Fragment_ClipPoly(dec, 3, verts[0], surfshader); - } -} - -static void Q1BSP_ClipDecalToNodes (model_t *mod, fragmentdecal_t *dec, mnode_t *node) -{ - mplane_t *splitplane; - float dist; - msurface_t *surf; - int i; - - if (node->contents < 0) - return; - - splitplane = node->plane; - dist = DotProduct (dec->center, splitplane->normal) - splitplane->dist; - - if (dist > dec->radius) - { - Q1BSP_ClipDecalToNodes (mod, dec, node->children[0]); - return; - } - if (dist < -dec->radius) - { - Q1BSP_ClipDecalToNodes (mod, dec, node->children[1]); - return; - } - -// mark the polygons - surf = mod->surfaces + node->firstsurface; - if (r_decal_noperpendicular.ival) - { - for (i=0 ; inumsurfaces ; i++, surf++) - { - if (surf->flags & SURF_PLANEBACK) - { - if (-DotProduct(surf->plane->normal, dec->normal) > -0.5) - continue; - } - else - { - if (DotProduct(surf->plane->normal, dec->normal) > -0.5) - continue; - } - Fragment_Mesh(dec, surf->mesh, surf->texinfo); - } - } - else - { - for (i=0 ; inumsurfaces ; i++, surf++) - Fragment_Mesh(dec, surf->mesh, surf->texinfo); - } - - Q1BSP_ClipDecalToNodes (mod, dec, node->children[0]); - Q1BSP_ClipDecalToNodes (mod, dec, node->children[1]); -} - -#ifdef RTLIGHTS -extern int sh_shadowframe; -#else -static int sh_shadowframe; -#endif -#ifdef Q3BSPS -static void Q3BSP_ClipDecalToNodes (fragmentdecal_t *dec, mnode_t *node) -{ - mplane_t *splitplane; - float dist; - msurface_t **msurf; - msurface_t *surf; - mleaf_t *leaf; - int i; - - if (node->contents != -1) - { - leaf = (mleaf_t *)node; - // mark the polygons - msurf = leaf->firstmarksurface; - for (i=0 ; inummarksurfaces ; i++, msurf++) - { - surf = *msurf; - - //only check each surface once. it can appear in multiple leafs. - if (surf->shadowframe == sh_shadowframe) - continue; - surf->shadowframe = sh_shadowframe; - - Fragment_Mesh(dec, surf->mesh, surf->texinfo); - } - return; - } - - splitplane = node->plane; - dist = DotProduct (dec->center, splitplane->normal) - splitplane->dist; - - if (dist > dec->radius) - { - Q3BSP_ClipDecalToNodes (dec, node->children[0]); - return; - } - if (dist < -dec->radius) - { - Q3BSP_ClipDecalToNodes (dec, node->children[1]); - return; - } - Q3BSP_ClipDecalToNodes (dec, node->children[0]); - Q3BSP_ClipDecalToNodes (dec, node->children[1]); -} -#endif - -void Mod_ClipDecal(struct model_s *mod, vec3_t center, vec3_t normal, vec3_t tangent1, vec3_t tangent2, float size, unsigned int surfflagmask, unsigned int surfflagmatch, void (*callback)(void *ctx, vec3_t *fte_restrict points, size_t numpoints, shader_t *shader), void *ctx) -{ //quad marks a full, independant quad - int p; - float r; - fragmentdecal_t dec; - - VectorCopy(center, dec.center); - VectorCopy(normal, dec.normal); - dec.radius = 0; - dec.callback = callback; - dec.ctx = ctx; - dec.surfflagmask = surfflagmask; - dec.surfflagmatch = surfflagmatch; - - VectorCopy(tangent1, dec.planenorm[0]); - VectorNegate(tangent1, dec.planenorm[1]); - VectorCopy(tangent2, dec.planenorm[2]); - VectorNegate(tangent2, dec.planenorm[3]); - VectorCopy(dec.normal, dec.planenorm[4]); - VectorNegate(dec.normal, dec.planenorm[5]); - for (p = 0; p < 6; p++) - { - r = sqrt(DotProduct(dec.planenorm[p], dec.planenorm[p])); - VectorScale(dec.planenorm[p], 1/r, dec.planenorm[p]); - r*= size/2; - if (r > dec.radius) - dec.radius = r; - dec.planedist[p] = -(r - DotProduct(dec.center, dec.planenorm[p])); - } - dec.numplanes = 6; - - sh_shadowframe++; - - if (!mod || mod->loadstate != MLS_LOADED || mod->type != mod_brush) - { - } - else if (mod->fromgame == fg_quake) - Q1BSP_ClipDecalToNodes(mod, &dec, mod->rootnode); -#ifdef Q3BSPS - else if (cl.worldmodel->fromgame == fg_quake3) - Q3BSP_ClipDecalToNodes(&dec, mod->rootnode); -#endif - -#ifdef TERRAIN - if (cl.worldmodel && cl.worldmodel->terrain) - Terrain_ClipDecal(&dec, center, dec.radius, mod); -#endif -} - #endif /* Rendering functions (Client only) @@ -2031,6 +2042,34 @@ void Q1BSP_Init(void) memset (mod_novis, 0xff, sizeof(mod_novis)); } +//sets up the functions a server needs. +//fills in bspfuncs_t +void Q1BSP_SetModelFuncs(model_t *mod) +{ +#ifndef CLIENTONLY + mod->funcs.FatPVS = Q1BSP_FatPVS; +#endif + mod->funcs.EdictInFatPVS = Q1BSP_EdictInFatPVS; + mod->funcs.FindTouchedLeafs = Q1BSP_FindTouchedLeafs; + mod->funcs.LightPointValues = NULL; + mod->funcs.StainNode = NULL; + mod->funcs.MarkLights = NULL; + + mod->funcs.ClusterForPoint = Q1BSP_ClusterForPoint; + mod->funcs.ClusterPVS = Q1BSP_ClusterPVS; + mod->funcs.NativeTrace = Q1BSP_Trace; + mod->funcs.PointContents = Q1BSP_PointContents; +} +#endif + +/* +Init stuff + +============================================================================== + +BSPX Stuff +*/ + typedef struct { char lumpname[24]; // up to 23 chars, zero-padded int fileofs; // from file start @@ -2099,22 +2138,3 @@ void Q1BSPX_Setup(model_t *mod, char *filebase, unsigned int filelen, lump_t *lu bspxheader = h; } - -//sets up the functions a server needs. -//fills in bspfuncs_t -void Q1BSP_SetModelFuncs(model_t *mod) -{ -#ifndef CLIENTONLY - mod->funcs.FatPVS = Q1BSP_FatPVS; -#endif - mod->funcs.EdictInFatPVS = Q1BSP_EdictInFatPVS; - mod->funcs.FindTouchedLeafs = Q1BSP_FindTouchedLeafs; - mod->funcs.LightPointValues = NULL; - mod->funcs.StainNode = NULL; - mod->funcs.MarkLights = NULL; - - mod->funcs.ClusterForPoint = Q1BSP_ClusterForPoint; - mod->funcs.ClusterPVS = Q1BSP_ClusterPVS; - mod->funcs.NativeTrace = Q1BSP_Trace; - mod->funcs.PointContents = Q1BSP_PointContents; -} diff --git a/engine/dotnet2005/ftequake.sln b/engine/dotnet2005/ftequake.sln index fb571455a..151f8a1e2 100644 --- a/engine/dotnet2005/ftequake.sln +++ b/engine/dotnet2005/ftequake.sln @@ -60,6 +60,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qi", "..\..\plugins\qi\qi.v EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models", "..\..\plugins\models\models.vcproj", "{E6CDA919-628B-45BF-A5DB-FB55179D6443}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "iqm", "..\..\iqm\iqm.vcproj", "{0AE4667A-A446-44E7-A758-69CF5D9AF8FC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution D3DDebug|Win32 = D3DDebug|Win32 @@ -689,7 +691,6 @@ Global {74542CA7-48C1-4664-9007-66F751131EA3}.MinGLRelease|Win32.ActiveCfg = Release|Win32 {74542CA7-48C1-4664-9007-66F751131EA3}.MinGLRelease|x64.ActiveCfg = Release|Win32 {74542CA7-48C1-4664-9007-66F751131EA3}.MRelease|Win32.ActiveCfg = Release|Win32 - {74542CA7-48C1-4664-9007-66F751131EA3}.MRelease|Win32.Build.0 = Release|Win32 {74542CA7-48C1-4664-9007-66F751131EA3}.MRelease|x64.ActiveCfg = Release|Win32 {74542CA7-48C1-4664-9007-66F751131EA3}.Release Dedicated Server|Win32.ActiveCfg = Release|Win32 {74542CA7-48C1-4664-9007-66F751131EA3}.Release Dedicated Server|x64.ActiveCfg = Release|Win32 @@ -805,7 +806,6 @@ Global {ED16B405-BDCD-4EB8-BF70-761964301368}.MinGLRelease|Win32.Build.0 = Release|Win32 {ED16B405-BDCD-4EB8-BF70-761964301368}.MinGLRelease|x64.ActiveCfg = Release|Win32 {ED16B405-BDCD-4EB8-BF70-761964301368}.MRelease|Win32.ActiveCfg = Release|Win32 - {ED16B405-BDCD-4EB8-BF70-761964301368}.MRelease|Win32.Build.0 = Release|Win32 {ED16B405-BDCD-4EB8-BF70-761964301368}.MRelease|x64.ActiveCfg = Release|Win32 {ED16B405-BDCD-4EB8-BF70-761964301368}.Release Dedicated Server|Win32.ActiveCfg = Release|Win32 {ED16B405-BDCD-4EB8-BF70-761964301368}.Release Dedicated Server|Win32.Build.0 = Release|Win32 @@ -897,7 +897,6 @@ Global {F756A3D2-025A-43D4-9829-4074753B774B}.MinGLRelease|x64.ActiveCfg = Release|x64 {F756A3D2-025A-43D4-9829-4074753B774B}.MinGLRelease|x64.Build.0 = Release|x64 {F756A3D2-025A-43D4-9829-4074753B774B}.MRelease|Win32.ActiveCfg = Release|Win32 - {F756A3D2-025A-43D4-9829-4074753B774B}.MRelease|Win32.Build.0 = Release|Win32 {F756A3D2-025A-43D4-9829-4074753B774B}.MRelease|x64.ActiveCfg = Release|x64 {F756A3D2-025A-43D4-9829-4074753B774B}.MRelease|x64.Build.0 = Release|x64 {F756A3D2-025A-43D4-9829-4074753B774B}.Release Dedicated Server|Win32.ActiveCfg = Release|x64 @@ -1011,6 +1010,60 @@ Global {E6CDA919-628B-45BF-A5DB-FB55179D6443}.VkRelease|Win32.Build.0 = Release|Win32 {E6CDA919-628B-45BF-A5DB-FB55179D6443}.VkRelease|x64.ActiveCfg = Release|x64 {E6CDA919-628B-45BF-A5DB-FB55179D6443}.VkRelease|x64.Build.0 = Release|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.D3DDebug|Win32.ActiveCfg = Debug|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.D3DDebug|Win32.Build.0 = Debug|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.D3DDebug|x64.ActiveCfg = Debug|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.D3DDebug|x64.Build.0 = Debug|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.D3DRelease|Win32.ActiveCfg = Release|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.D3DRelease|Win32.Build.0 = Release|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.D3DRelease|x64.ActiveCfg = Release|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.D3DRelease|x64.Build.0 = Release|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.Debug Dedicated Server|Win32.ActiveCfg = Debug|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.Debug Dedicated Server|x64.ActiveCfg = Debug|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.Debug Dedicated Server|x64.Build.0 = Debug|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.Debug|Win32.ActiveCfg = Debug|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.Debug|Win32.Build.0 = Debug|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.Debug|x64.ActiveCfg = Debug|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.Debug|x64.Build.0 = Debug|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.GLDebug|Win32.ActiveCfg = Debug|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.GLDebug|Win32.Build.0 = Debug|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.GLDebug|x64.ActiveCfg = Debug|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.GLDebug|x64.Build.0 = Debug|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.GLRelease|Win32.ActiveCfg = Release|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.GLRelease|Win32.Build.0 = Release|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.GLRelease|x64.ActiveCfg = Release|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.GLRelease|x64.Build.0 = Release|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.MDebug|Win32.ActiveCfg = Debug|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.MDebug|Win32.Build.0 = Debug|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.MDebug|x64.ActiveCfg = Debug|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.MDebug|x64.Build.0 = Debug|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.MinGLDebug|Win32.ActiveCfg = Debug|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.MinGLDebug|Win32.Build.0 = Debug|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.MinGLDebug|x64.ActiveCfg = Debug|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.MinGLDebug|x64.Build.0 = Debug|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.MinGLRelease|Win32.ActiveCfg = Release|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.MinGLRelease|Win32.Build.0 = Release|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.MinGLRelease|x64.ActiveCfg = Release|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.MinGLRelease|x64.Build.0 = Release|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.MRelease|Win32.ActiveCfg = Release|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.MRelease|Win32.Build.0 = Release|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.MRelease|x64.ActiveCfg = Release|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.MRelease|x64.Build.0 = Release|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.Release Dedicated Server|Win32.ActiveCfg = Release|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.Release Dedicated Server|x64.ActiveCfg = Release|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.Release Dedicated Server|x64.Build.0 = Release|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.Release|Win32.ActiveCfg = Release|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.Release|Win32.Build.0 = Release|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.Release|x64.ActiveCfg = Release|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.Release|x64.Build.0 = Release|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.VkDebug|Win32.ActiveCfg = Debug|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.VkDebug|Win32.Build.0 = Debug|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.VkDebug|x64.ActiveCfg = Debug|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.VkDebug|x64.Build.0 = Debug|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.VkRelease|Win32.ActiveCfg = Release|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.VkRelease|Win32.Build.0 = Release|Win32 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.VkRelease|x64.ActiveCfg = Release|x64 + {0AE4667A-A446-44E7-A758-69CF5D9AF8FC}.VkRelease|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/engine/gl/gl_alias.c b/engine/gl/gl_alias.c index 9f0aeb4b0..e5ee28a3d 100644 --- a/engine/gl/gl_alias.c +++ b/engine/gl/gl_alias.c @@ -602,6 +602,9 @@ static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, e plskin = sk->qwskin; } } + else if (inf->geomset < MAX_GEOMSETS && 0 != inf->geomid) + return NULL; + /*hexen2 feature: global skins */ if (inf->numskins < e->skinnum && e->skinnum >= r_globalskin_first.ival && e->skinnum < r_globalskin_first.ival+r_globalskin_count.ival) diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index ae32cc15f..f58f0affe 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -4862,7 +4862,7 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g { newvals = PR_GetStringOfs(prinst, OFS_PARM2); if (idx >= mod->numentityinfo) - Z_ReallocElements(&mod->entityinfo, &mod->numentityinfo, idx+64, sizeof(*mod->entityinfo)); + Z_ReallocElements((void**)&mod->entityinfo, &mod->numentityinfo, idx+64, sizeof(*mod->entityinfo)); mod->entityinfo[idx].keyvals = Z_StrDup(newvals); } else @@ -6198,7 +6198,7 @@ void CL_Parse_BrushEdit(void) if (id > 0xffff) return; if (id >= mod->numentityinfo) - Z_ReallocElements(&mod->entityinfo, &mod->numentityinfo, id+64, sizeof(*mod->entityinfo)); + Z_ReallocElements((void**)&mod->entityinfo, &mod->numentityinfo, id+64, sizeof(*mod->entityinfo)); if (id < mod->numentityinfo) { if (!ignore) diff --git a/engine/gl/gl_hlmdl.c b/engine/gl/gl_hlmdl.c index 4b300c63b..7e1865e18 100644 --- a/engine/gl/gl_hlmdl.c +++ b/engine/gl/gl_hlmdl.c @@ -396,6 +396,21 @@ int HLMDL_FrameForName(model_t *mod, const char *name) return -1; } +qboolean HLMDL_GetModelEvent(model_t *model, int animation, int eventidx, float *timestamp, int *eventcode, char **eventdata) +{ + hlmodel_t *mc = Mod_Extradata(model); + hlmdl_header_t *h = mc->header; + hlmdl_event_t *ev; + hlmdl_sequencelist_t *seq = animation + (hlmdl_sequencelist_t*)((char*)h+h->seqindex); + if (animation < 0 || animation >= h->numseq || eventidx < 0 || eventidx >= seq->num_events) + return false; + ev = eventidx + (hlmdl_event_t*)((char*)h+seq->ofs_events); + *timestamp = ev->pose / seq->timing; + *eventcode = ev->code; + *eventdata = ev->data; + return true; +} + int HLMDL_BoneForName(model_t *mod, const char *name) { int i; diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index 93621d6ca..27d9dd9f1 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -52,7 +52,9 @@ void CM_Shutdown(void); void Mod_LoadSpriteShaders(model_t *spr); qboolean QDECL Mod_LoadSpriteModel (model_t *mod, void *buffer, size_t fsize); qboolean QDECL Mod_LoadSprite2Model (model_t *mod, void *buffer, size_t fsize); -qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize); +#ifdef Q1BSPS +static qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize); +#endif #ifdef Q2BSPS qboolean QDECL Mod_LoadQ2BrushModel (model_t *mod, void *buffer, size_t fsize); #endif @@ -77,18 +79,18 @@ extern cvar_t r_loadlits; extern cvar_t gl_specular; #endif extern cvar_t r_fb_bmodels; -mesh_t nullmesh; void Mod_SortShaders(model_t *mod); void Mod_LoadAliasShaders(model_t *mod); #ifdef RUNTIMELIGHTING model_t *lightmodel; -struct relight_ctx_s *lightcontext; -int numlightdata; -qboolean writelitfile; +static struct relight_ctx_s *lightcontext; +static int numlightdata; +static qboolean writelitfile; long relitsurface; -void Mod_UpdateLightmap(int snum) +#ifndef MULTITHREAD +static void Mod_UpdateLightmap(int snum) { msurface_t *s; if (lightmodel) @@ -107,6 +109,7 @@ void Mod_UpdateLightmap(int snum) } } #endif +#endif static void Mod_MemList_f(void) { @@ -261,12 +264,13 @@ static void Mod_BlockTextureColour_f (void) #endif -#if defined(RUNTIMELIGHTING) && defined(MULTITHREAD) -void *relightthread[8]; -unsigned int relightthreads; -volatile qboolean wantrelight; +#if defined(RUNTIMELIGHTING) +#if defined(MULTITHREAD) +static void *relightthread[8]; +static unsigned int relightthreads; +static volatile qboolean wantrelight; -int RelightThread(void *arg) +static int RelightThread(void *arg) { int surf; void *threadctx = malloc(lightthreadctxsize); @@ -287,8 +291,8 @@ int RelightThread(void *arg) free(threadctx); return 0; } -#else -void *lightmainthreadctx; +#endif +static void *lightmainthreadctx; #endif void Mod_Think (void) @@ -707,7 +711,9 @@ void Mod_Init (qboolean initial) Mod_ClearAll(); //shouldn't be needed Mod_Purge(MP_RESET);//shouldn't be needed mod_numknown = 0; +#ifdef Q1BSPS Q1BSP_Init(); +#endif Cmd_AddCommand("mod_memlist", Mod_MemList_f); #ifndef SERVERONLY @@ -758,12 +764,14 @@ void Mod_Init (qboolean initial) Mod_RegisterModelFormatText(NULL, "Doom3 (cm)", "CM", D3_LoadMap_CollisionMap); #endif +#ifdef Q1BSPS //q1-based formats Mod_RegisterModelFormatMagic(NULL, "Quake1 2PSB Map(bsp)", BSPVERSION_LONG1, Mod_LoadBrushModel); Mod_RegisterModelFormatMagic(NULL, "Quake1 BSP2 Map(bsp)", BSPVERSION_LONG2, Mod_LoadBrushModel); Mod_RegisterModelFormatMagic(NULL, "Half-Life Map (bsp)", 30, Mod_LoadBrushModel); Mod_RegisterModelFormatMagic(NULL, "Quake1 Map (bsp)", 29, Mod_LoadBrushModel); Mod_RegisterModelFormatMagic(NULL, "Quake1 Prerelease Map (bsp)", 28, Mod_LoadBrushModel); +#endif } } @@ -1120,7 +1128,7 @@ Mod_LoadModel Loads a model into the cache ================== */ -void Mod_LoadModelWorker (void *ctx, void *data, size_t a, size_t b) +static void Mod_LoadModelWorker (void *ctx, void *data, size_t a, size_t b) { model_t *mod = ctx; enum mlverbosity_e verbose = a; @@ -1436,10 +1444,9 @@ static const char *Mod_RemapBuggyTexture(const char *name, const qbyte *data, un } #endif - -void Mod_FinishTexture(texture_t *tx, const char *loadname, qboolean safetoloadfromwads) -{ #ifndef SERVERONLY +static void Mod_FinishTexture(texture_t *tx, const char *loadname, qboolean safetoloadfromwads) +{ extern cvar_t gl_shadeq1_name; char altname[MAX_QPATH]; char *star; @@ -1520,9 +1527,7 @@ void Mod_FinishTexture(texture_t *tx, const char *loadname, qboolean safetoloadf R_BuildLegacyTexnums(tx->shader, origname, loadname, maps, 0, fmt, tx->width, tx->height, tx->mips, tx->palette); } BZ_Free(tx->mips[0]); -#endif } -#ifndef SERVERONLY static void Mod_LoadMiptex(model_t *loadmodel, texture_t *tx, miptex_t *mt) { unsigned int size = @@ -1559,7 +1564,7 @@ static void Mod_LoadMiptex(model_t *loadmodel, texture_t *tx, miptex_t *mt) Mod_LoadTextures ================= */ -qboolean Mod_LoadTextures (model_t *loadmodel, qbyte *mod_base, lump_t *l) +static qboolean Mod_LoadTextures (model_t *loadmodel, qbyte *mod_base, lump_t *l) { int i, j, num, max, altmax; miptex_t *mt; @@ -2224,7 +2229,7 @@ void Mod_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean Mod_LoadVisibility ================= */ -void Mod_LoadVisibility (model_t *loadmodel, qbyte *mod_base, lump_t *l, qbyte *ptr, size_t len) +static void Mod_LoadVisibility (model_t *loadmodel, qbyte *mod_base, lump_t *l, qbyte *ptr, size_t len) { if (!ptr) { @@ -2462,7 +2467,7 @@ qboolean Mod_LoadVertexNormals (model_t *loadmodel, qbyte *mod_base, lump_t *l) Mod_LoadSubmodels ================= */ -qboolean Mod_LoadSubmodels (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean *hexen2map) +static qboolean Mod_LoadSubmodels (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean *hexen2map) { dq1model_t *inq; dh2model_t *inh; @@ -2611,7 +2616,7 @@ qboolean Mod_LoadEdges (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean Mod_LoadTexinfo ================= */ -qboolean Mod_LoadTexinfo (model_t *loadmodel, qbyte *mod_base, lump_t *l) +static qboolean Mod_LoadTexinfo (model_t *loadmodel, qbyte *mod_base, lump_t *l) { texinfo_t *in; mtexinfo_t *out; @@ -2652,7 +2657,7 @@ qboolean Mod_LoadTexinfo (model_t *loadmodel, qbyte *mod_base, lump_t *l) out->texture = r_notexture_mip; // texture not found out->flags = 0; } - if (!strncmp(out->texture->name, "scroll", 6) || ((*out->texture->name == '*' || *out->texture->name == '{' || *out->texture->name == '!') && !strncmp(out->texture->name+1, "scroll", 6))) + else if (!strncmp(out->texture->name, "scroll", 6) || ((*out->texture->name == '*' || *out->texture->name == '{' || *out->texture->name == '!') && !strncmp(out->texture->name+1, "scroll", 6))) out->flags |= TI_FLOWING; } @@ -2721,7 +2726,7 @@ void CalcSurfaceExtents (model_t *mod, msurface_t *s); Mod_LoadFaces ================= */ -qboolean Mod_LoadFaces (model_t *loadmodel, qbyte *mod_base, lump_t *l, lump_t *lightlump, qboolean lm) +static qboolean Mod_LoadFaces (model_t *loadmodel, qbyte *mod_base, lump_t *l, lump_t *lightlump, qboolean lm) { dsface_t *ins; dlface_t *inl; @@ -3113,7 +3118,7 @@ static void Mod_Batches_BuildModelMeshes(model_t *mod, int maxverts, int maxindi } //q1 autoanimates. if the frame is set, it uses the alternate animation. -void Mod_UpdateBatchShader_Q1 (struct batch_s *batch) +static void Mod_UpdateBatchShader_Q1 (struct batch_s *batch) { texture_t *base = batch->texture; int reletive; @@ -3144,7 +3149,7 @@ void Mod_UpdateBatchShader_Q1 (struct batch_s *batch) } //q2 has direct control over the texture frames used, but typically has the client generate the frame (different flags autogenerate different ranges). -void Mod_UpdateBatchShader_Q2 (struct batch_s *batch) +static void Mod_UpdateBatchShader_Q2 (struct batch_s *batch) { texture_t *base = batch->texture; int reletive; @@ -3721,7 +3726,7 @@ static qboolean Mod_LoadNodes (model_t *loadmodel, qbyte *mod_base, lump_t *l, i out->firstsurface = LittleLong (in->firstface); out->numsurfaces = LittleLong (in->numfaces); - + for (j=0 ; j<2 ; j++) { p = LittleLong (in->children[j]); @@ -3760,7 +3765,7 @@ static qboolean Mod_LoadNodes (model_t *loadmodel, qbyte *mod_base, lump_t *l, i out->firstsurface = (unsigned short)LittleShort (in->firstface); out->numsurfaces = (unsigned short)LittleShort (in->numfaces); - + for (j=0 ; j<2 ; j++) { p = LittleShort (in->children[j]); @@ -3988,13 +3993,13 @@ static qboolean Mod_LoadLeafs (model_t *loadmodel, qbyte *mod_base, lump_t *l, i //these are used to boost other info sizes -int numsuplementryplanes; -int numsuplementryclipnodes; -void *suplementryclipnodes; -void *suplementryplanes; -void *crouchhullfile; +static int numsuplementryplanes; +static int numsuplementryclipnodes; +static void *suplementryclipnodes; +static void *suplementryplanes; +static void *crouchhullfile; -void Mod_LoadCrouchHull(model_t *loadmodel) +static void Mod_LoadCrouchHull(model_t *loadmodel) { int i, h; int numsm; @@ -4065,7 +4070,7 @@ void Mod_LoadCrouchHull(model_t *loadmodel) Mod_LoadClipnodes ================= */ -qboolean Mod_LoadClipnodes (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean lm, qboolean hexen2map) +static qboolean Mod_LoadClipnodes (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean lm, qboolean hexen2map) { dsclipnode_t *ins; dlclipnode_t *inl; @@ -4320,7 +4325,7 @@ Mod_MakeHull0 Deplicate the drawing hull structure as a clipping hull ================= */ -void Mod_MakeHull0 (model_t *loadmodel) +static void Mod_MakeHull0 (model_t *loadmodel) { mnode_t *in, *child; mclipnode_t *out; @@ -4452,7 +4457,7 @@ qboolean Mod_LoadSurfedges (model_t *loadmodel, qbyte *mod_base, lump_t *l) Mod_LoadPlanes ================= */ -qboolean Mod_LoadPlanes (model_t *loadmodel, qbyte *mod_base, lump_t *l) +static qboolean Mod_LoadPlanes (model_t *loadmodel, qbyte *mod_base, lump_t *l) { int i, j; mplane_t *out; @@ -4550,7 +4555,7 @@ static void Q1BSP_StainNode (mnode_t *node, float *parms) } #endif -void Mod_FixupNodeMinsMaxs (mnode_t *node, mnode_t *parent) +static void Mod_FixupNodeMinsMaxs (mnode_t *node, mnode_t *parent) { if (!node) return; @@ -4733,6 +4738,8 @@ void ModBrush_LoadGLStuff(void *ctx, void *data, size_t a, size_t b) #endif } +#ifdef Q1BSPS + struct vispatch_s { void *fileptr; @@ -4816,7 +4823,7 @@ static void Mod_FindVisPatch(struct vispatch_s *patch, model_t *mod, size_t leaf Mod_LoadBrushModel ================= */ -qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize) +static qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize) { struct vispatch_s vispatch; int i, j; @@ -5109,6 +5116,7 @@ TRACE(("LoadBrushModel %i\n", __LINE__)); #endif return true; } +#endif /* ============================================================================== diff --git a/engine/gl/gl_model.h b/engine/gl/gl_model.h index fbcc2af4d..beda31773 100644 --- a/engine/gl/gl_model.h +++ b/engine/gl/gl_model.h @@ -66,8 +66,9 @@ struct doll_s *rag_createdollfromstring(struct model_s *mod, const char *fname, struct world_s; void rag_doallanimations(struct world_s *world); void rag_removedeltaent(lerpents_t *le); -void rag_updatedeltaent(entity_t *ent, lerpents_t *le); +void rag_updatedeltaent(struct world_s *w, entity_t *ent, lerpents_t *le); void rag_lerpdeltaent(lerpents_t *le, unsigned int bonecount, short *newstate, float frac, short *oldstate); +void skel_reset(struct world_s *world); typedef struct mesh_s { @@ -107,7 +108,6 @@ typedef struct mesh_s byte_vec4_t *bonenums; vec4_t *boneweights; } mesh_t; -extern mesh_t nullmesh; /* batches are generated for each shader/ent as required. @@ -831,7 +831,7 @@ typedef enum {fg_quake, fg_quake2, fg_quake3, fg_halflife, fg_new, fg_doom, fg_d #define MFH2_SCARAB (1u<<21) // white transparent particles with little gravity #define MFH2_ACIDBALL (1u<<22) // Green drippy acid shit #define MFH2_BLOODSHOT (1u<<23) // Blood rain shot trail -#define MFH2_ROCKET (1u<<31) // spider blood (remapped from MF_ROCKET, to avoid dlight issues) +#define MFH2_SPIDERBLOOD (1u<<31) // spider blood (remapped from MF_ROCKET, to avoid dlight issues) typedef union { struct { diff --git a/engine/gl/gl_rlight.c b/engine/gl/gl_rlight.c index 54d6ac509..5f01a2c80 100644 --- a/engine/gl/gl_rlight.c +++ b/engine/gl/gl_rlight.c @@ -1591,10 +1591,10 @@ static float *GLRecursiveLightPoint3C (model_t *mod, mnode_t *node, vec3_t start front = DotProduct (start, plane->normal) - plane->dist; back = DotProduct (end, plane->normal) - plane->dist; side = front < 0; - + if ( (back < 0) == side) return GLRecursiveLightPoint3C (mod, node->children[side], start, end); - + frac = front / (front-back); mid[0] = start[0] + (end[0] - start[0])*frac; mid[1] = start[1] + (end[1] - start[1])*frac; @@ -1604,10 +1604,10 @@ static float *GLRecursiveLightPoint3C (model_t *mod, mnode_t *node, vec3_t start r = GLRecursiveLightPoint3C (mod, node->children[side], start, mid); if (r && r[0]+r[1]+r[2] >= 0) return r; // hit something - + if ( (back < 0) == side ) return NULL; // didn't hit anuthing - + // check for impact on this node VectorCopy (mid, lightspot); lightplane = plane; @@ -1626,7 +1626,7 @@ static float *GLRecursiveLightPoint3C (model_t *mod, mnode_t *node, vec3_t start if (s < surf->texturemins[0] || t < surf->texturemins[1]) continue; - + ds = s - surf->texturemins[0]; dt = t - surf->texturemins[1]; diff --git a/engine/gl/model_hl.h b/engine/gl/model_hl.h index 146a9891e..d498f4d8e 100644 --- a/engine/gl/model_hl.h +++ b/engine/gl/model_hl.h @@ -192,7 +192,9 @@ typedef struct char name[32]; float timing; int loop; - int unknown1[4]; + int unknown1[2]; + int num_events; + int ofs_events; int numframes; int unknown2[2]; int motiontype; @@ -209,6 +211,14 @@ typedef struct int unknown9[4]; } hlmdl_sequencelist_t; +typedef struct +{ + int pose; + int code; + int unknown1; + char data[64]; +} hlmdl_event_t; + /* ----------------------------------------------------------------------------------------------------------------------- sequence groups @@ -289,6 +299,7 @@ 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); +qboolean HLMDL_GetModelEvent(model_t *model, int animation, int eventidx, float *timestamp, int *eventcode, char **eventdata); int HLMDL_GetNumBones(model_t *mod, qboolean tagstoo); int HLMDL_GetBoneParent(model_t *mod, int bonenum); const char *HLMDL_GetBoneName(model_t *mod, int bonenum); diff --git a/engine/http/httpclient.c b/engine/http/httpclient.c index e7b62a355..acec63f14 100644 --- a/engine/http/httpclient.c +++ b/engine/http/httpclient.c @@ -1379,7 +1379,10 @@ static int DL_Thread_Work(void *arg) #ifdef NPFTE //the plugin doesn't have a download loop if (dl->notifycomplete) + { dl->notifycomplete(dl); + dl->notifycomplete = NULL; + } if (dl->file) VFS_CLOSE(dl->file); #else @@ -1452,6 +1455,7 @@ struct dl_download *DL_Create(const char *url) strcpy(newdl->url, url); newdl->poll = DL_Decide; newdl->sizelimit = 0x80000000u; //some sanity limit. + newdl->qdownload.method = DL_HTTP; if (!newdl->poll(newdl)) { @@ -1488,6 +1492,10 @@ void DL_Close(struct dl_download *dl) if (dl->threadctx) Sys_WaitOnThread(dl->threadctx); #endif + if (dl->file && dl->file->Seek) + VFS_SEEK(dl->file, 0); + if (dl->notifycomplete) + dl->notifycomplete(dl); if (dl->abort) dl->abort(dl); if (dl->file) @@ -1542,7 +1550,6 @@ struct dl_download *HTTP_CL_Get(const char *url, const char *localfile, void (*N if (!cls.download && localfile && !newdl->isquery) { cls.download = &newdl->qdownload; - newdl->qdownload.method = DL_HTTP; if (*newdl->localname) Q_strncpyz(newdl->qdownload.localname, newdl->localname, sizeof(newdl->qdownload.localname)); else @@ -1614,10 +1621,6 @@ void HTTP_CL_Think(void) if (!dl->poll(dl)) { *link = dl->next; - if (dl->file && dl->file->Seek) - VFS_SEEK(dl->file, 0); - if (dl->notifycomplete) - dl->notifycomplete(dl); DL_Close(dl); continue; } diff --git a/engine/qclib/pr_edict.c b/engine/qclib/pr_edict.c index 885cc0554..4937d857d 100644 --- a/engine/qclib/pr_edict.c +++ b/engine/qclib/pr_edict.c @@ -2529,6 +2529,8 @@ int PR_ReallyLoadProgs (progfuncs_t *progfuncs, const char *filename, progstate_ int stringadjust; + char *mainstringtable, *newstringtable; + current_progstate = progstate; strcpy(current_progstate->filename, filename); @@ -2792,6 +2794,9 @@ retry: len=sizeof(char)*pr_progs->numstrings; s = PRAddressableExtend(progfuncs, pr_strings, len, 0); + + newstringtable = s; + mainstringtable = progfuncs->funcs.stringtable; pr_strings = (char *)s; len=sizeof(float)*pr_progs->numglobals; diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index 5641362ff..35783c939 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -670,6 +670,7 @@ int QCC_PR_IntConstExpr(void); #ifndef COMMONINLINES pbool QCC_PR_CheckImmediate (const char *string); pbool QCC_PR_CheckToken (const char *string); +pbool QCC_PR_PeekToken (const char *string); pbool QCC_PR_CheckName (const char *string); void QCC_PR_Expect (const char *string); pbool QCC_PR_CheckKeyword(int keywordenabled, const char *string); @@ -1073,6 +1074,16 @@ static bool inline QCC_PR_CheckToken (char *string) QCC_PR_Lex (); return true; } +static bool inline QCC_PR_PeekToken (char *string) +{ + if (pr_token_type != tt_punct) + return false; + + if (STRCMP (string, pr_token)) + return false; + + return true; +} static void inline QCC_PR_Expect (const char *string) { diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index 67d601137..9e82c1404 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -3,6 +3,8 @@ #include "qcc.h" #include +#define WARN_IMPLICITVARIANTCAST 0 + //FIXME: #define IAMNOTLAZY #define SUPPORTINLINE @@ -191,6 +193,7 @@ void QCC_PR_DiscardRef(QCC_ref_t *ref); QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *type, pbool dowrap); const char *QCC_VarAtOffset(QCC_sref_t ref, unsigned int size); QCC_sref_t QCC_EvaluateCast(QCC_sref_t src, QCC_type_t *cast, pbool implicit); +void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t def, unsigned int flags); QCC_statement_t *QCC_Generate_OP_IFNOT(QCC_sref_t e, pbool preserve); QCC_statement_t *QCC_Generate_OP_IF(QCC_sref_t e, pbool preserve); @@ -5887,60 +5890,8 @@ QCC_sref_t QCC_PR_ParseFunctionCall (QCC_ref_t *funcref) //warning, the func cou if (p) { if (typecmp(e->cast, p)) - /*if (e->type->type != ev_integer && p->type != ev_function) - if (e->type->type != ev_function && p->type != ev_integer) - if ( e->type->type != p->type )*/ { e = QCC_PR_BuildRef(¶mbuf[arg], REF_GLOBAL, QCC_EvaluateCast(QCC_RefToDef(e, true), p, true), nullsref, p, true); - -#if 0 - if (p->type == ev_integer && e.cast->type == ev_float) //convert float -> int... is this a constant? - e = QCC_PR_Statement(pr_opcodes+OP_CONV_FTOI, e, nullsref, NULL); - else if (p->type == ev_float && e.cast->type == ev_integer) //convert float -> int... is this a constant? - e = QCC_PR_Statement(pr_opcodes+OP_CONV_ITOF, e, nullsref, NULL); - else if ((p->type == ev_function || p->type == ev_field || p->type == ev_string || p->type == ev_pointer || p->type == ev_entity) && QCC_SRef_IsNull(e)) - { //you're allowed to use int 0 to pass a null function/field/string/pointer/entity - //this is basically because __NULL__ is defined as 0i (int 0) - //WARNING: field 0 is actually a valid field, and is commonly modelindex. - } - else if ((p->type == ev_field || p->type == ev_pointer) && e.cast->type == p->type && (p->aux_type->type == ev_variant || e.cast->aux_type->type == ev_variant || p->aux_type->type == ev_void || e.cast->aux_type->type == ev_void)) - { //allow passing variant fields etc (also allow .void or *void as universal/variant field/pointer types) - } - else if ( (p->type == ev_accessor && p->parentclass->type == e.cast->type) - || (e.cast->type == ev_accessor && e.cast->parentclass->type == p->type)) - { - } - else if ((p->type == ev_vector) && QCC_SRef_IsNull(e)) - { - //also allow it for vector types too, but make sure the entire vector is valid. - e = QCC_MakeVectorConst(0, 0, 0); - } - else if (p->type != ev_variant && e.cast->type != ev_variant) //can cast to variant whatever happens - { - QCC_type_t *inh; - for (inh = e.cast->parentclass; inh; inh = inh->parentclass) - { - if (!typecmp(inh, p)) - break; - } - if (!inh) - { - char typebuf1[1024]; - char typebuf2[1024]; - if (flag_laxcasts || (p->type == ev_function && e.cast->type == ev_function)) - { - - QCC_PR_ParseWarning(WARN_LAXCAST, "type mismatch on parm %i: %s should be %s", arg+1, TypeName(e.cast, typebuf1, sizeof(typebuf1)), TypeName(p, typebuf2, sizeof(typebuf2))); - QCC_PR_ParsePrintSRef(WARN_LAXCAST, func); - } - else - { - QCC_PR_ParseWarning (ERR_TYPEMISMATCHPARM, "type mismatch on parm %i: %s should be %s", arg+1, TypeName(e.cast, typebuf1, sizeof(typebuf1)), TypeName(p, typebuf2, sizeof(typebuf2))); - QCC_PR_ParsePrintSRef(ERR_TYPEMISMATCHPARM, func); - } - } - } -#endif } } param[arg] = e; @@ -7053,6 +7004,73 @@ vectorarrayindex: return r; } +QCC_sref_t QCC_PR_GenerateVector(QCC_sref_t x, QCC_sref_t y, QCC_sref_t z) +{ + QCC_sref_t d; + + if ((x.cast->type != ev_float && x.cast->type != ev_integer) || + (y.cast->type != ev_float && y.cast->type != ev_integer) || + (z.cast->type != ev_float && z.cast->type != ev_integer)) + { + QCC_PR_ParseError(ERR_TYPEMISMATCH, "Argument not a single numeric value in vector constructor"); + return QCC_MakeVectorConst(0, 0, 0); + } + + //return a constant if we can. + if (x.sym->constant && y.sym->constant && z.sym->constant) + { + d = QCC_MakeVectorConst( + (x.cast->type==ev_float)?x.sym->symboldata[x.ofs]._float:x.sym->symboldata[x.ofs]._int, + (y.cast->type==ev_float)?y.sym->symboldata[y.ofs]._float:y.sym->symboldata[y.ofs]._int, + (z.cast->type==ev_float)?z.sym->symboldata[z.ofs]._float:z.sym->symboldata[z.ofs]._int); + QCC_FreeTemp(x); + QCC_FreeTemp(y); + QCC_FreeTemp(z); + return d; + } + if (QCC_SRef_IsNull(y) && QCC_SRef_IsNull(z)) + { + QCC_FreeTemp(y); + QCC_FreeTemp(z); + return QCC_PR_StatementFlags(pr_opcodes + OP_MUL_VF, QCC_MakeVectorConst(1, 0, 0), QCC_SupplyConversion(x, ev_float, true), NULL, 0); + } + if (QCC_SRef_IsNull(x) && QCC_SRef_IsNull(z)) + { + QCC_FreeTemp(x); + QCC_FreeTemp(z); + return QCC_PR_StatementFlags(pr_opcodes + OP_MUL_VF, QCC_MakeVectorConst(0, 1, 0), QCC_SupplyConversion(y, ev_float, true), NULL, 0); + } + if (QCC_SRef_IsNull(x) && QCC_SRef_IsNull(y)) + { + QCC_FreeTemp(x); + QCC_FreeTemp(y); + return QCC_PR_StatementFlags(pr_opcodes + OP_MUL_VF, QCC_MakeVectorConst(0, 0, 1), QCC_SupplyConversion(z, ev_float, true), NULL, 0); + } + + //pack the variables into a vector + d = QCC_GetTemp(type_vector); + d.cast = type_float; + if (x.cast->type == ev_float) + QCC_PR_StatementFlags(pr_opcodes + OP_STORE_F, x, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT); + else + QCC_PR_StatementFlags(pr_opcodes+OP_STORE_IF, x, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT); + d.ofs++; + if (y.cast->type == ev_float) + QCC_PR_StatementFlags(pr_opcodes + OP_STORE_F, y, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT); + else + QCC_PR_StatementFlags(pr_opcodes+OP_STORE_IF, y, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT); + d.ofs++; + if (z.cast->type == ev_float) + QCC_PR_StatementFlags(pr_opcodes + OP_STORE_F, z, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT); + else + QCC_PR_StatementFlags(pr_opcodes+OP_STORE_IF, z, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT); + d.ofs++; + d.ofs -= 3; + d.cast = type_vector; + + return d; +} + /* ============ PR_ParseValue @@ -7105,67 +7123,7 @@ QCC_ref_t *QCC_PR_ParseRefValue (QCC_ref_t *refbuf, QCC_type_t *assumeclass, pbo QCC_PR_Expect("]"); - if ((x.cast->type != ev_float && x.cast->type != ev_integer) || - (y.cast->type != ev_float && y.cast->type != ev_integer) || - (z.cast->type != ev_float && z.cast->type != ev_integer)) - { - QCC_PR_ParseError(ERR_TYPEMISMATCH, "Argument not a single numeric value in vector constructor"); - return QCC_DefToRef(refbuf, QCC_MakeVectorConst(0, 0, 0)); - } - - //return a constant if we can. - if (x.sym->constant && y.sym->constant && z.sym->constant) - { - d = QCC_MakeVectorConst( - (x.cast->type==ev_float)?x.sym->symboldata[x.ofs]._float:x.sym->symboldata[x.ofs]._int, - (y.cast->type==ev_float)?y.sym->symboldata[y.ofs]._float:y.sym->symboldata[y.ofs]._int, - (z.cast->type==ev_float)?z.sym->symboldata[z.ofs]._float:z.sym->symboldata[z.ofs]._int); - QCC_FreeTemp(x); - QCC_FreeTemp(y); - QCC_FreeTemp(z); - return QCC_DefToRef(refbuf, d); - } - if (QCC_SRef_IsNull(y) && QCC_SRef_IsNull(z)) - { - QCC_FreeTemp(y); - QCC_FreeTemp(z); - return QCC_DefToRef(refbuf, QCC_PR_StatementFlags(pr_opcodes + OP_MUL_VF, QCC_MakeVectorConst(1, 0, 0), QCC_SupplyConversion(x, ev_float, true), NULL, 0)); - } - if (QCC_SRef_IsNull(x) && QCC_SRef_IsNull(z)) - { - QCC_FreeTemp(x); - QCC_FreeTemp(z); - return QCC_DefToRef(refbuf, QCC_PR_StatementFlags(pr_opcodes + OP_MUL_VF, QCC_MakeVectorConst(0, 1, 0), QCC_SupplyConversion(y, ev_float, true), NULL, 0)); - } - if (QCC_SRef_IsNull(x) && QCC_SRef_IsNull(y)) - { - QCC_FreeTemp(x); - QCC_FreeTemp(y); - return QCC_DefToRef(refbuf, QCC_PR_StatementFlags(pr_opcodes + OP_MUL_VF, QCC_MakeVectorConst(0, 0, 1), QCC_SupplyConversion(z, ev_float, true), NULL, 0)); - } - - //pack the variables into a vector - d = QCC_GetTemp(type_vector); - d.cast = type_float; - if (x.cast->type == ev_float) - QCC_PR_StatementFlags(pr_opcodes + OP_STORE_F, x, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT); - else - QCC_PR_StatementFlags(pr_opcodes+OP_STORE_IF, x, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT); - d.ofs++; - if (y.cast->type == ev_float) - QCC_PR_StatementFlags(pr_opcodes + OP_STORE_F, y, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT); - else - QCC_PR_StatementFlags(pr_opcodes+OP_STORE_IF, y, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT); - d.ofs++; - if (z.cast->type == ev_float) - QCC_PR_StatementFlags(pr_opcodes + OP_STORE_F, z, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT); - else - QCC_PR_StatementFlags(pr_opcodes+OP_STORE_IF, z, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT); - d.ofs++; - d.ofs -= 3; - d.cast = type_vector; - - return QCC_DefToRef(refbuf, d); + return QCC_DefToRef(refbuf, QCC_PR_GenerateVector(x,y,z)); } if (QCC_PR_CheckToken("::")) @@ -7501,7 +7459,18 @@ QCC_sref_t QCC_EvaluateCast(QCC_sref_t src, QCC_type_t *cast, pbool implicit) /*variants can be cast from/to anything without warning, even implicitly. FIXME: size issues*/ else if (totype == ev_variant || src.cast->type == ev_variant || (totype == ev_field && totype == src.cast->type && (tmp->aux_type->type == ev_variant || src.cast->aux_type->type == ev_variant))) + { src.cast = cast; + + if (implicit && typecmp_lax(src.cast, cast)) + { + char typea[256]; + char typeb[256]; + TypeName(src.cast, typea, sizeof(typea)); + TypeName(cast, typeb, sizeof(typeb)); + QCC_PR_ParseWarning(WARN_IMPLICITVARIANTCAST, "Implicit cast from %s to %s", typea, typeb); + } + } /*these casts are fine when explicit*/ else if ( /*you may explicitly cast between pointers and ints (strings count as pointers - WARNING: some strings may not be expressable as pointers)*/ @@ -7706,34 +7675,34 @@ QCC_ref_t *QCC_PR_RefTerm (QCC_ref_t *retbuf, unsigned int exprflags) newtype = QCC_PR_ParseType(false, true); if (newtype) { + /*if (QCC_PR_Check ("[")) + { + QCC_PR_Expect ("]"); + QCC_PR_Expect(")"); + QCC_PR_Expect("{"); + QCC_PR_Expect ("}"); + return array; + }*/ QCC_PR_Expect (")"); - if (newtype->type == ev_function && pr_token_type == tt_punct && !strcmp(pr_token, "{")) + if (QCC_PR_PeekToken("{")) { - //save some state of the parent - QCC_def_t *firstlocal = pr.local_head.nextlocal; - QCC_def_t *lastlocal = pr.local_tail; - QCC_function_t *parent = pr_scope; - QCC_statement_t *patch; - - //FIXME: make sure gotos/labels/cases/continues/breaks are not broken by this. - - //generate a goto statement around the nested function, so that nothing is hurt. - e = nullsref; - e.cast = type_float; - patch = QCC_Generate_OP_GOTO(); - e = QCC_MakeIntConst(QCC_PR_ParseImmediateStatements (NULL, newtype, false) - functions); - e.cast = newtype; - patch->a.ofs = &statements[numstatements] - patch; - - //make sure parent state is restored properly. - pr.local_head.nextlocal = firstlocal; - pr.local_tail = lastlocal; - pr_scope = parent; - } - else if ((newtype->type == ev_struct || newtype->type == ev_union) && pr_token_type == tt_punct && !strcmp(pr_token, "{")) - { - //FIXME - QCC_PR_ParseError(0, "struct immediates are not supported at this time\n"); + if (newtype->type == ev_vector) + { + QCC_sref_t x,y,z; + QCC_PR_Expect("{"); + x = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA); + QCC_PR_Expect(","); + y = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA); + QCC_PR_Expect(","); + z = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA); + QCC_PR_Expect ("}"); + return QCC_DefToRef(retbuf, QCC_PR_GenerateVector(x,y,z)); + } + else + { + e = QCC_GetTemp(newtype); + QCC_PR_ParseInitializerType(0, NULL, e, 0); + } } else { @@ -8612,11 +8581,13 @@ QCC_sref_t QCC_StoreSRefToRef(QCC_ref_t *dest, QCC_sref_t source, pbool readable break; t = t->parentclass; } - if (!t && !(source.cast->type == ev_pointer && dest->cast->type == ev_pointer && (source.cast->aux_type->type == ev_void || source.cast->aux_type->type == ev_variant))) + if (!t && !(source.cast->type == ev_pointer && dest->cast->type == ev_pointer && (source.cast->aux_type->type == ev_void || source.cast->aux_type->type == ev_variant)) && source.cast->type != ev_variant && dest->cast->type != ev_variant) { //extra check to allow void*->any* char typea[256]; char typeb[256]; - if ((dest->cast->type == ev_float || dest->cast->type == ev_integer) && (source.cast->type == ev_float || source.cast->type == ev_integer)) + if (source.cast->type == ev_variant || dest->cast->type == ev_variant) + QCC_PR_ParseWarning(WARN_IMPLICITVARIANTCAST, "type mismatch: %s %s to %s %s.%s", typea, QCC_GetSRefName(source), typeb, QCC_GetSRefName(dest->base), QCC_GetSRefName(dest->index)); + else if ((dest->cast->type == ev_float || dest->cast->type == ev_integer) && (source.cast->type == ev_float || source.cast->type == ev_integer)) source = QCC_SupplyConversion(source, dest->cast->type, true); else { @@ -11582,6 +11553,9 @@ void QCC_WriteAsmFunction(QCC_function_t *sc, unsigned int firststatement, QCC_d QCC_type_t *type; char typebuf[512]; + if (sc->parentscope) //don't print dupes. + return; + if (flag_guiannotate) QCC_WriteGUIAsmFunction(sc, firststatement); @@ -13052,6 +13026,7 @@ void QCC_PR_ExpandUnionToFields(QCC_type_t *type, unsigned int *fields) #define PIF_WRAP 1 //new initialisation is meant to wrap an existing one. #define PIF_STRONGER 2 //previous initialisation was weak. +//basedef can be null, meaning its initialising a temp void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t def, unsigned int flags) { QCC_sref_t tmp; @@ -13112,7 +13087,7 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d strncpy(fname, QCC_PR_ParseName(), sizeof(fname)); //if the builtin already exists, just use that dfunction instead - if (basedef->initialized) + if (basedef && basedef->initialized) { if (*fname) { @@ -13158,34 +13133,36 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d } else { - if (flags&PIF_WRAP) + if (basedef) { - if (!basedef->initialized || !def.sym->symboldata[def.ofs]._int) - QCC_PR_ParseErrorPrintSRef (ERR_REDECLARATION, def, "wrapper function does not wrap anything"); - } - else if (basedef->initialized == 1 && !(flags & PIF_STRONGER)) - { - //normally this is an error, but to aid supporting new stuff with old, we convert it into a warning if a vanilla(ish) qc function replaces extension builtins. - //the qc function is the one that is used, but there is a warning so you know how to gain efficiency. - int bi = -1; - if (def.cast->type == ev_function && !arraysize) + if (flags&PIF_WRAP) { - if (!strcmp(defname, "anglemod") || !strcmp(defname, "crossproduct")) - bi = def.sym->symboldata[def.ofs]._int; + if (!basedef->initialized || !def.sym->symboldata[def.ofs]._int) + QCC_PR_ParseErrorPrintSRef (ERR_REDECLARATION, def, "wrapper function does not wrap anything"); } - if (bi <= 0 || bi >= numfunctions) - bi = 0; - else - bi = functions[bi].code; - if (bi < 0) + else if (basedef->initialized == 1 && !(flags & PIF_STRONGER)) { - QCC_PR_ParseWarning(WARN_NOTSTANDARDBEHAVIOUR, "%s already declared as a builtin", defname); - QCC_PR_ParsePrintSRef(WARN_NOTSTANDARDBEHAVIOUR, def); - basedef->initialized = 3; + //normally this is an error, but to aid supporting new stuff with old, we convert it into a warning if a vanilla(ish) qc function replaces extension builtins. + //the qc function is the one that is used, but there is a warning so you know how to gain efficiency. + int bi = -1; + if (def.cast->type == ev_function && !arraysize) + { + if (!strcmp(defname, "anglemod") || !strcmp(defname, "crossproduct")) + bi = def.sym->symboldata[def.ofs]._int; + } + if (bi <= 0 || bi >= numfunctions) + bi = 0; + else + bi = functions[bi].code; + if (bi < 0) + { + QCC_PR_ParseWarning(WARN_NOTSTANDARDBEHAVIOUR, "%s already declared as a builtin", defname); + QCC_PR_ParsePrintSRef(WARN_NOTSTANDARDBEHAVIOUR, def); + basedef->initialized = 3; + } + else + QCC_PR_ParseErrorPrintSRef (ERR_REDECLARATION, def, "redeclaration of function body"); } - else - QCC_PR_ParseErrorPrintSRef (ERR_REDECLARATION, def, "redeclaration of function body"); - } if (pr_scope) @@ -13213,7 +13190,7 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d f = QCC_PR_ParseImmediateStatements (def.sym, type, flags&PIF_WRAP); //allow dupes if its a builtin - if (!f->code && basedef->initialized) + if (basedef && !f->code && basedef->initialized) { for (i = 1; i < numfunctions; i++) { @@ -13290,7 +13267,7 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d tmp = QCC_EvaluateCast(tmp, type, true); } - if (!basedef->scope || basedef->constant || basedef->isstatic) + if (basedef && (!basedef->scope || basedef->constant || basedef->isstatic)) { tmp.sym->referenced = true; if (!tmp.sym->constant) @@ -13368,6 +13345,17 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d else QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_FNC], rhs, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB)); } + else if (def.cast->type == ev_string) + { + rhs.cast = def.cast = type_string; + if (type->size - i == 1) + { + QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_S], rhs, def, NULL, STFL_PRESERVEB)); + return; + } + else + QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_S], rhs, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB)); + } else if (def.cast->type == ev_entity) { rhs.cast = def.cast = type_entity; diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index 5daa88356..192e3eb83 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -3886,6 +3886,15 @@ pbool QCC_PR_CheckToken (const char *string) return true; } +pbool QCC_PR_PeekToken (const char *string) +{ + if (pr_token_type != tt_punct) + return false; + if (STRCMP (string, pr_token)) + return false; + return true; +} + pbool QCC_PR_CheckImmediate (const char *string) { if (pr_token_type != tt_immediate) diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index 79df3457d..ebb125d09 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -1462,7 +1462,7 @@ pbool QCC_WriteData (int crc) } } */ - if (!def->referenced) + if (!def->symbolheader->used) { int wt = def->constant?WARN_NOTREFERENCEDCONST:WARN_NOTREFERENCED; pr_scope = def->scope; @@ -1504,7 +1504,7 @@ pbool QCC_WriteData (int crc) if (def->strip || !def->symbolheader->used) { #ifdef DEBUG_DUMP - printf("code: %s:%i: strip %s %s@%i;\n", strings+def->s_file, def->s_line, def->type->name, def->name, def->ofs); + printf("code: %s:%i: strip %s %s@%i;\n", def->filen, def->s_line, def->type->name, def->name, def->ofs); #endif continue; } @@ -1550,9 +1550,9 @@ pbool QCC_WriteData (int crc) #ifdef DEBUG_DUMP if (def->scope) - printf("code: %s:%i: strip local %s %s@%i;\n", strings+def->s_file, def->s_line, def->type->name, def->name, def->ofs); + printf("code: %s:%i: strip local %s %s@%i;\n", def->filen, def->s_line, def->type->name, def->name, def->ofs); else if (def->constant) - printf("code: %s:%i: strip const %s %s@%i;\n", strings+def->s_file, def->s_line, def->type->name, def->name, def->ofs); + printf("code: %s:%i: strip const %s %s@%i;\n", def->filen, def->s_line, def->type->name, def->name, def->ofs); #endif continue; } @@ -1590,19 +1590,19 @@ pbool QCC_WriteData (int crc) #ifdef DEBUG_DUMP if ((dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)) == ev_string) - printf("code: %s:%i: %s%s%s %s@%i = \"%s\"\n", strings+def->s_file, def->s_line, dd->type&DEF_SAVEGLOBAL?"save ":"nosave ", dd->type&DEF_SHARED?"shared ":"", basictypenames[dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)], strings+dd->s_name, dd->ofs, ((unsigned)def->symboldata[def->ofs].string>=(unsigned)strofs)?"???":(strings + def->symboldata[def->ofs].string)); + printf("code: %s:%i: %s%s%s %s@%i = \"%s\"\n", def->filen, def->s_line, dd->type&DEF_SAVEGLOBAL?"save ":"nosave ", dd->type&DEF_SHARED?"shared ":"", basictypenames[dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)], strings+dd->s_name, dd->ofs, ((unsigned)def->symboldata[def->ofs].string>=(unsigned)strofs)?"???":(strings + def->symboldata[def->ofs].string)); else if ((dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)) == ev_float) - printf("code: %s:%i: %s%s%s %s@%i = %g\n", strings+def->s_file, def->s_line, dd->type&DEF_SAVEGLOBAL?"save ":"nosave ", dd->type&DEF_SHARED?"shared ":"", basictypenames[dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)], strings+dd->s_name, dd->ofs, def->symboldata[def->ofs]._float); + printf("code: %s:%i: %s%s%s %s@%i = %g\n", def->filen, def->s_line, dd->type&DEF_SAVEGLOBAL?"save ":"nosave ", dd->type&DEF_SHARED?"shared ":"", basictypenames[dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)], strings+dd->s_name, dd->ofs, def->symboldata[def->ofs]._float); else if ((dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)) == ev_integer) - printf("code: %s:%i: %s%s%s %s@%i = %i\n", strings+def->s_file, def->s_line, dd->type&DEF_SAVEGLOBAL?"save ":"nosave ", dd->type&DEF_SHARED?"shared ":"", basictypenames[dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)], strings+dd->s_name, dd->ofs, def->symboldata[def->ofs]._int); + printf("code: %s:%i: %s%s%s %s@%i = %i\n", def->filen, def->s_line, dd->type&DEF_SAVEGLOBAL?"save ":"nosave ", dd->type&DEF_SHARED?"shared ":"", basictypenames[dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)], strings+dd->s_name, dd->ofs, def->symboldata[def->ofs]._int); else if ((dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)) == ev_vector) - printf("code: %s:%i: %s%s%s %s@%i = '%g %g %g'\n", strings+def->s_file, def->s_line, dd->type&DEF_SAVEGLOBAL?"save ":"nosave ", dd->type&DEF_SHARED?"shared ":"", basictypenames[dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)], strings+dd->s_name, dd->ofs, def->symboldata[def->ofs].vector[0], def->symboldata[def->ofs].vector[1], def->symboldata[def->ofs].vector[2]); + printf("code: %s:%i: %s%s%s %s@%i = '%g %g %g'\n", def->filen, def->s_line, dd->type&DEF_SAVEGLOBAL?"save ":"nosave ", dd->type&DEF_SHARED?"shared ":"", basictypenames[dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)], strings+dd->s_name, dd->ofs, def->symboldata[def->ofs].vector[0], def->symboldata[def->ofs].vector[1], def->symboldata[def->ofs].vector[2]); else if ((dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)) == ev_function) - printf("code: %s:%i: %s%s%s %s@%i = %i(%s)\n", strings+def->s_file, def->s_line, dd->type&DEF_SAVEGLOBAL?"save ":"nosave ", dd->type&DEF_SHARED?"shared ":"", basictypenames[dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)], strings+dd->s_name, dd->ofs, def->symboldata[def->ofs].function, def->symboldata[def->ofs].function >= numfunctions?"???":functions[def->symboldata[def->ofs].function].name); + printf("code: %s:%i: %s%s%s %s@%i = %i(%s)\n", def->filen, def->s_line, dd->type&DEF_SAVEGLOBAL?"save ":"nosave ", dd->type&DEF_SHARED?"shared ":"", basictypenames[dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)], strings+dd->s_name, dd->ofs, def->symboldata[def->ofs].function, def->symboldata[def->ofs].function >= numfunctions?"???":functions[def->symboldata[def->ofs].function].name); else if ((dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)) == ev_field) - printf("code: %s:%i: %s%s%s %s@%i = @%i\n", strings+def->s_file, def->s_line, dd->type&DEF_SAVEGLOBAL?"save ":"nosave ", dd->type&DEF_SHARED?"shared ":"", basictypenames[dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)], strings+dd->s_name, dd->ofs, def->symboldata[def->ofs]._int); + printf("code: %s:%i: %s%s%s %s@%i = @%i\n", def->filen, def->s_line, dd->type&DEF_SAVEGLOBAL?"save ":"nosave ", dd->type&DEF_SHARED?"shared ":"", basictypenames[dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)], strings+dd->s_name, dd->ofs, def->symboldata[def->ofs]._int); else - printf("code: %s:%i: %s%s%s %s@%i\n", strings+def->s_file, def->s_line, dd->type&DEF_SAVEGLOBAL?"save ":"nosave ", dd->type&DEF_SHARED?"shared ":"", basictypenames[dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)], strings+dd->s_name, dd->ofs); + printf("code: %s:%i: %s%s%s %s@%i\n", def->filen, def->s_line, dd->type&DEF_SAVEGLOBAL?"save ":"nosave ", dd->type&DEF_SHARED?"shared ":"", basictypenames[dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)], strings+dd->s_name, dd->ofs); #endif } diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index b579fb40c..5c0a81da2 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -506,9 +506,18 @@ model_t *QDECL SVPR_GetCModel(world_t *w, int modelindex) { if ((unsigned int)modelindex < MAX_PRECACHE_MODELS) { + model_t *mod; if (!sv.models[modelindex] && sv.strings.model_precache[modelindex]) sv.models[modelindex] = Mod_ForName(Mod_FixName(sv.strings.model_precache[modelindex], sv.modelname), MLV_WARN); - return sv.models[modelindex]; + mod = sv.models[modelindex]; + if (mod && mod->loadstate != MLS_LOADED) + { + if (mod->loadstate == MLS_LOADING) + COM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING); + if (mod->loadstate != MLS_LOADED) + mod = NULL; //gah, it failed! + } + return mod; } else return NULL; @@ -528,7 +537,7 @@ static void QDECL SVPR_Get_FrameState(world_t *w, wedict_t *ent, framestate_t *f #if defined(SKELETALOBJECTS) || defined(RAGDOLL) if (ent->xv->skeletonindex) - skel_lookup(w->progs, ent->xv->skeletonindex, fstate); + skel_lookup(w, ent->xv->skeletonindex, fstate); #endif } @@ -10253,6 +10262,9 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"skel_delete", PF_skel_delete, 0, 0, 0, 275, D("void(float skel)", "Deletes a skeletal object. The actual delete is delayed, allowing the skeletal object to be deleted in an entity's predraw function yet still be valid by the time the addentity+renderscene builtins need it. Also uninstanciates any ragdoll currently in effect on the skeletal object.")}, // (FTE_CSQC_SKELETONOBJECTS) {"frameforname", PF_frameforname, 0, 0, 0, 276, D("float(float modidx, string framename)", "Looks up a framegroup from a model by name, avoiding the need for hardcoding. Returns -1 on error.")},// (FTE_CSQC_SKELETONOBJECTS) {"frameduration", PF_frameduration, 0, 0, 0, 277, D("float(float modidx, float framenum)", "Retrieves the duration (in seconds) of the specified framegroup.")},// (FTE_CSQC_SKELETONOBJECTS) + {"processmodelevents",PF_processmodelevents,0, 0, 0, 0, D("void(float modidx, float framenum, __inout float basetime, float targettime, void(float timestamp, int code, string data) callback)", "Calls a callback for each event that has been reached. Basetime is set to targettime.")}, + {"getnextmodelevent",PF_getnextmodelevent,0, 0, 0, 0, D("float(float modidx, float framenum, __inout float basetime, float targettime, __out int code, __out string data)", "Reports the next event within a model's animation. Returns a boolean if an event was found between basetime and targettime. Writes to basetime,code,data arguments (if an event was found, basetime is set to the event's time, otherwise to targettime).\nWARNING: this builtin cannot deal with multiple events with the same timestamp (only the first will be reported).")}, + {"getmodeleventidx",PF_getmodeleventidx,0, 0, 0, 0, D("float(float modidx, float framenum, int eventidx, __out float timestamp, __out int code, __out string data)", "Reports an indexed event within a model's animation. Writes to timestamp,code,data arguments on success. Returns false if the animation/event/model was out of range/invalid. Does not consider looping animations (retry from index 0 if it fails and you know that its a looping animation). This builtin is more annoying to use than getnextmodelevent, but can be made to deal with multiple events with the exact same timestamp.")}, {"crossproduct", PF_crossproduct, 0, 0, 0, 0, D("#define dotproduct(v1,v2) ((vector)(v1)*(vector)(v2))\nvector(vector v1, vector v2)", "Small helper function to calculate the crossproduct of two vectors.")}, @@ -10349,8 +10361,8 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"drawrotsubpic", PF_Fixme, 0, 0, 0, 0, D("void(vector pivot, vector mins, vector maxs, string pic, vector txmin, vector txsize, vector rgb, vector alphaandangles)", "Overcomplicated draw function for over complicated people. Positions follow drawrotpic, while texture coords follow drawsubpic. Due to argument count limitations in builtins, the alpha value and angles are combined into separate fields of a vector (tip: use fteqcc's [alpha, angle] feature.")}, //330 - {"getstati", PF_Fixme, 0, 0, 0, 330, D("float(float stnum)", "Retrieves the numerical value of the given EV_INTEGER or EV_ENTITY stat (converted to a float).")},// (EXT_CSQC) - {"getstatf", PF_Fixme, 0, 0, 0, 331, D("#define getstatbits getstatf\nfloat(float stnum, optional float firstbit, optional float bitcount)", "Retrieves the numerical value of the given EV_FLOAT stat. If firstbit and bitcount are specified, retrieves the upper bits of the STAT_ITEMS stat.")},// (EXT_CSQC) + {"getstati", PF_Fixme, 0, 0, 0, 330, D("#define getstati_punf(stnum) (float)(__variant)getstati(stnum)\nint(float stnum)", "Retrieves the numerical value of the given EV_INTEGER or EV_ENTITY stat (converted to a float).")},// (EXT_CSQC) + {"getstatf", PF_Fixme, 0, 0, 0, 331, D("#define getstatbits getstatf\nfloat(float stnum, optional float firstbit, optional float bitcount)", "Retrieves the numerical value of the given EV_FLOAT stat. If firstbit and bitcount are specified, retrieves the upper bits of the STAT_ITEMS stat (converted into a float, so there are no VM dependancies).")},// (EXT_CSQC) {"getstats", PF_Fixme, 0, 0, 0, 332, D("string(float stnum)", "Retrieves the value of the given EV_STRING stat, as a tempstring.\nOlder engines may use 4 consecutive integer stats, with a limit of 15 chars (yes, really. 15.), but "FULLENGINENAME" uses a separate namespace for string stats and has a much higher length limit.")}, {"getplayerstat", PF_Fixme, 0, 0, 0, 0, D("__variant(float playernum, float statnum, float stattype)", "Retrieves a specific player's stat, matching the type specified on the server. This builtin is primarily intended for mvd playback where ALL players are known. For EV_ENTITY, world will be returned if the entity is not in the pvs, use type-punning with EV_INTEGER to get the entity number if you just want to see if its set. STAT_ITEMS should be queried as an EV_INTEGER on account of runes and items2 being packed into the upper bits.")}, diff --git a/engine/server/progdefs.h b/engine/server/progdefs.h index 344bdb00a..3cddfb16c 100644 --- a/engine/server/progdefs.h +++ b/engine/server/progdefs.h @@ -421,10 +421,13 @@ typedef struct float animate; qboolean draw:1; qboolean orient:1; + qboolean isoffset:1; int orientpeer; //ode info int geomshape; + float relmatrix[12]; + float inverserelmatrix[12]; vec3_t dimensions; float mass; } rbebodyinfo_t; diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index 9b06668af..2ae57c969 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -3240,7 +3240,7 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli framestate_t fs; fs.skeltype = SKEL_IDENTITY; fs.bonecount = 0; - skel_lookup(sv.world.progs, ent->xv->skeletonindex, &fs); + skel_lookup(&sv.world, ent->xv->skeletonindex, &fs); if (fs.skeltype == SKEL_RELATIVE && fs.bonecount) { Bones_To_PosQuat4(fs.bonecount, fs.bonestate, AllocateBoneSpace(pack, state->bonecount = fs.bonecount, &state->boneoffset)); diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index a15fd9e1c..5fcd70448 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -1135,6 +1135,7 @@ void SVC_GetInfo (char *challenge, int fullstatus) { //dpmaster support char response[MAX_UDP_PACKET]; + char protocolname[MAX_QPATH]; client_t *cl; int numclients = 0; @@ -1164,6 +1165,7 @@ void SVC_GetInfo (char *challenge, int fullstatus) else gamestatus = ""; + COM_ParseOut(com_protocolname.string, protocolname, sizeof(protocolname)); resp = response; @@ -1184,7 +1186,7 @@ void SVC_GetInfo (char *challenge, int fullstatus) //this is a DP protocol query, so some QW fields are not needed Info_RemoveKey(resp, "maxclients"); //replaced with sv_maxclients Info_RemoveKey(resp, "map"); //replaced with mapname - Info_SetValueForKey(resp, "gamename", com_protocolname.string, sizeof(response) - (resp-response)); + Info_SetValueForKey(resp, "gamename", protocolname, sizeof(response) - (resp-response)); Info_SetValueForKey(resp, "modname", FS_GetGamedir(true), sizeof(response) - (resp-response)); // Info_SetValueForKey(resp, "gamedir", FS_GetGamedir(true), sizeof(response) - (resp-response)); Info_SetValueForKey(resp, "protocol", va("%d", NQ_NETCHAN_VERSION), sizeof(response) - (resp-response)); diff --git a/engine/server/sv_move.c b/engine/server/sv_move.c index 8466a8f2a..9e11d3eab 100644 --- a/engine/server/sv_move.c +++ b/engine/server/sv_move.c @@ -65,7 +65,8 @@ qboolean World_CheckBottom (world_t *world, wedict_t *ent, vec3_t up) sign = (up[a2]>0)?1:-1; VectorAdd (ent->v->origin, ent->v->mins, mins); - if (world->worldmodel->fromgame == fg_quake) +#ifdef Q1BSPS + if (world->worldmodel->fromgame == fg_quake || world->worldmodel->fromgame == fg_halflife) { //quake's hulls are weird. sizes are defined as from mins to mins+hullsize. the actual maxs is ignored other than for its size. hull_t *hull; @@ -75,6 +76,7 @@ qboolean World_CheckBottom (world_t *world, wedict_t *ent, vec3_t up) VectorAdd (maxs, hull->clip_maxs, maxs); } else +#endif VectorAdd (ent->v->origin, ent->v->maxs, maxs); // if all of the points under the corners are solid world, don't bother diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 0c5b9eacd..cee75d557 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -2404,6 +2404,7 @@ qboolean SV_Physics (void) int i; qboolean moved = false; int maxtics; + double trueframetime = host_frametime; //keep gravity tracking the cvar properly movevars.gravity = sv_gravity.value; @@ -2425,6 +2426,7 @@ qboolean SV_Physics (void) if (host_frametime < sv_maxtic.value && realtime) { // sv.time+=host_frametime; + host_frametime = trueframetime; return false; //don't bother with the whole server thing for a bit longer } if (host_frametime > sv_maxtic.value) @@ -2446,6 +2448,7 @@ qboolean SV_Physics (void) default: break; } + host_frametime = trueframetime; return true; } @@ -2581,6 +2584,7 @@ qboolean SV_Physics (void) sv.world.physicstime += host_frametime; } + host_frametime = trueframetime; return moved; } #endif diff --git a/engine/vk/vk_init.c b/engine/vk/vk_init.c index a75f937a2..12e9eed09 100644 --- a/engine/vk/vk_init.c +++ b/engine/vk/vk_init.c @@ -254,15 +254,20 @@ static qboolean VK_CreateSwapChain(void) vk.dopresent(NULL); //make sure they're all pushed through. - VkAssert(vkGetPhysicalDeviceSurfaceFormatsKHR(vk.gpu, vk.surface, &fmtcount, NULL)); - surffmts = malloc(sizeof(VkSurfaceFormatKHR)*fmtcount); - VkAssert(vkGetPhysicalDeviceSurfaceFormatsKHR(vk.gpu, vk.surface, &fmtcount, surffmts)); + if (!vk.surface) + return true; + else + { + VkAssert(vkGetPhysicalDeviceSurfaceFormatsKHR(vk.gpu, vk.surface, &fmtcount, NULL)); + surffmts = malloc(sizeof(VkSurfaceFormatKHR)*fmtcount); + VkAssert(vkGetPhysicalDeviceSurfaceFormatsKHR(vk.gpu, vk.surface, &fmtcount, surffmts)); - VkAssert(vkGetPhysicalDeviceSurfacePresentModesKHR(vk.gpu, vk.surface, &presentmodes, NULL)); - presentmode = malloc(sizeof(VkPresentModeKHR)*presentmodes); - VkAssert(vkGetPhysicalDeviceSurfacePresentModesKHR(vk.gpu, vk.surface, &presentmodes, presentmode)); + VkAssert(vkGetPhysicalDeviceSurfacePresentModesKHR(vk.gpu, vk.surface, &presentmodes, NULL)); + presentmode = malloc(sizeof(VkPresentModeKHR)*presentmodes); + VkAssert(vkGetPhysicalDeviceSurfacePresentModesKHR(vk.gpu, vk.surface, &presentmodes, presentmode)); - vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk.gpu, vk.surface, &surfcaps); + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk.gpu, vk.surface, &surfcaps); + } swapinfo.surface = vk.surface; swapinfo.minImageCount = surfcaps.minImageCount+vk.triplebuffer; @@ -2110,11 +2115,19 @@ qboolean VK_SCR_GrabBackBuffer(void) if (vk.frame) //erk, we already have one... return true; - + + RSpeedRemark(); VK_FencedCheck(); + if (!vk.surface) + { +// if (Media_Capturing() != 2) +// Cmd_ExecuteString("quit force\n", RESTRICT_LOCAL); + return false; //headless... + } + if (!vk.unusedframes) { struct vkframe *newframe = Z_Malloc(sizeof(*vk.frame)); @@ -2745,8 +2758,14 @@ qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*creat const char *extensions[8]; qboolean nvglsl = false; uint32_t extensions_count = 0; - extensions[extensions_count++] = sysextname; - extensions[extensions_count++] = VK_KHR_SURFACE_EXTENSION_NAME; + qboolean headless = false; + if (sysextname) + { + extensions[extensions_count++] = sysextname; + extensions[extensions_count++] = VK_KHR_SURFACE_EXTENSION_NAME; + } + else + headless = true; if (vk_debug.ival) extensions[extensions_count++] = VK_EXT_DEBUG_REPORT_EXTENSION_NAME; @@ -2877,18 +2896,21 @@ qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*creat vkGetPhysicalDeviceProperties(devs[i], &props); vkGetPhysicalDeviceQueueFamilyProperties(devs[i], &queue_count, NULL); - for (j = 0; j < queue_count; j++) + if (!headless) { - VkBool32 supportsPresent; - VkAssert(vkGetPhysicalDeviceSurfaceSupportKHR(devs[i], j, vk.surface, &supportsPresent)); - if (supportsPresent) - break; //okay, this one should be usable - } - if (j == queue_count) - { - //no queues can present to that surface, so I guess we can't use that device - Con_DPrintf("vulkan: ignoring device %s as it can't present to window\n", props.deviceName); - continue; + for (j = 0; j < queue_count; j++) + { + VkBool32 supportsPresent = false; + VkAssert(vkGetPhysicalDeviceSurfaceSupportKHR(devs[i], j, vk.surface, &supportsPresent)); + if (supportsPresent) + break; //okay, this one should be usable + } + if (j == queue_count) + { + //no queues can present to that surface, so I guess we can't use that device + Con_DPrintf("vulkan: ignoring device %s as it can't present to window\n", props.deviceName); + continue; + } } if (!vk.gpu) @@ -3022,8 +3044,11 @@ qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*creat { for (i = 0; i < queue_count; i++) { - VkBool32 supportsPresent; - VkAssert(vkGetPhysicalDeviceSurfaceSupportKHR(vk.gpu, i, vk.surface, &supportsPresent)); + VkBool32 supportsPresent = false; + if (headless) + supportsPresent = true; //won't be used anyway. + else + VkAssert(vkGetPhysicalDeviceSurfaceSupportKHR(vk.gpu, i, vk.surface, &supportsPresent)); if ((queueprops[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && supportsPresent) {