From 09ec3bf6eac2ec4309916a2d8858197aadb313e1 Mon Sep 17 00:00:00 2001 From: Spoike Date: Tue, 18 Apr 2017 11:12:17 +0000 Subject: [PATCH] added explicit 'blend rtsmoke' particles that are intended to lit by rtlights without needing to override shaders. addtrisoup_simple now supports line lists too. fix a memory leak in the sound code. fix chunked http download corruption in certain cases. reworked the networking associated with entity editing. now carries ids instead of indexes (qc isn't aware of them). 'map blarg.map' will now auto-create a dummy map if it does not already exist. this should make it easier to get started with map editing. fix some issues with banning yourself - loopback can now mute etc themselves, but not ban themselves. downloads/updates menu now handles plugin detection. user-installed plugins will result in a prompt allowing the user to enable/disable/delete them. This makes using plugins more explicit. revamped help menu to remove m_state fix silly ode copypasta bug, added preliminary bullet code in case anyone is interested. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5090 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- build_wip.sh | 14 +- engine/Makefile | 24 +- engine/client/cl_demo.c | 1 - engine/client/cl_main.c | 10 +- engine/client/cl_parse.c | 6 +- engine/client/cl_plugin.inc | 6 +- engine/client/cl_screen.c | 3 +- engine/client/client.h | 3 +- engine/client/image.c | 10 +- engine/client/keys.c | 5 +- engine/client/m_download.c | 199 ++- engine/client/m_items.c | 7 - engine/client/m_master.c | 2 - engine/client/m_mp3.c | 4 - engine/client/m_multi.c | 4 - engine/client/m_options.c | 12 +- engine/client/m_script.c | 1 - engine/client/m_single.c | 5 - engine/client/menu.c | 205 ++- engine/client/menu.h | 8 +- engine/client/p_script.c | 34 + engine/client/pr_csqc.c | 12 +- engine/client/pr_menu.c | 1 + engine/client/renderer.c | 2 +- engine/client/snd_dma.c | 1 + engine/client/snd_wasapi.c | 2 +- engine/client/sys_droid.c | 3 + engine/client/sys_plugfte.c | 21 + engine/client/sys_sdl.c | 3 + engine/client/sys_xdk.c | 7 +- engine/client/wad.h | 2 + engine/client/winquake.rc | 4 + engine/common/bothdefs.h | 32 +- engine/common/com_phys_ode.c | 4 +- engine/common/common.c | 60 +- engine/common/common.h | 5 +- engine/common/particles.h | 13 +- engine/common/pr_bgcmd.c | 19 +- engine/common/pr_common.h | 7 +- engine/common/world.h | 2 +- engine/common/zone.c | 8 +- engine/common/zone.h | 10 +- engine/dotnet2005/ftequake.vcproj | 196 +++ engine/gl/gl_backend.c | 2 +- engine/gl/gl_heightmap.c | 149 +- engine/gl/gl_model.c | 33 +- engine/gl/gl_model.h | 2 +- engine/gl/gl_rlight.c | 13 +- engine/gl/gl_shader.c | 12 +- engine/gl/gl_vidcommon.c | 20 +- engine/gl/r_bishaders.h | 38 +- engine/gl/shader.h | 8 +- engine/http/httpclient.c | 61 +- engine/http/iwebiface.c | 4 +- engine/qclib/qcc_pr_comp.c | 214 +-- engine/qclib/qccmain.c | 6 +- engine/server/pr_cmds.c | 25 +- engine/server/progs.h | 1 + engine/server/server.h | 4 +- engine/server/sv_ccmds.c | 31 +- engine/server/sv_mvd.c | 2 +- engine/server/sv_phys.c | 2 +- engine/server/sv_sys_unix.c | 3 + engine/server/world.c | 1 + engine/shaders/glsl/defaultsprite.glsl | 3 +- engine/shaders/glsl/rtlight.glsl | 35 +- engine/web/ftejslib.js | 4 +- engine/web/sys_web.c | 3 + plugins/Makefile | 76 +- plugins/bullet/bulletplug.cpp | 1766 ++++++++++++++++++++++++ 70 files changed, 2944 insertions(+), 551 deletions(-) create mode 100644 plugins/bullet/bulletplug.cpp diff --git a/build_wip.sh b/build_wip.sh index d12fff9e..805ef700 100755 --- a/build_wip.sh +++ b/build_wip.sh @@ -2,12 +2,10 @@ START=$(date +%s) SVNROOT=$(cd "$(dirname "$(readlink "$BASH_SOURCE")")" && pwd) -FTECONFIG=$SVNROOT/build_cfg.sh +FTECONFIG=$SVNROOT/build.cfg HOME=`echo ~` BASE=$SVNROOT/.. -#how many cpu cores do you have? -THREADS="-j 4" #set this if you want non-default branding, for customised builds. #export BRANDING=wastes @@ -23,12 +21,15 @@ PLUGINS_LINUXx86="qi ezhud xmpp irc" PLUGINS_LINUXx64="qi ezhud xmpp irc" PLUGINS_LINUXx32="qi ezhud xmpp irc" PLUGINS_WINDOWS="avplug ode qi ezhud xmpp irc" +THREADS="-j 4" ########### NaCL stuff NACL_SDK_ROOT=/opt/nacl_sdk/pepper_31/ if [ -e $FTECONFIG ]; then . $FTECONFIG +else + echo "WARNING: $FTECONFIG does not exist yet." fi export NACL_SDK_ROOT @@ -143,6 +144,10 @@ if [ "$BUILD_WINDOWS" != "n" ]; then NATIVE_PLUGINS="$PLUGINS_WINDOWS" build "Windows 32-bit" win32 FTE_TARGET=win32 CFLAGS="$WARNINGLEVEL" sv-rel gl-rel vk-rel mingl-rel m-rel d3d-rel qcc-rel qccgui-scintilla qccgui-dbg gl-dbg sv-dbg plugins-dbg plugins-rel NATIVE_PLUGINS="$PLUGINS_WINDOWS" NATIVE_PLUGINS="$PLUGINS_WINDOWS" build "Windows 64-bit" win64 FTE_TARGET=win64 CFLAGS="$WARNINGLEVEL" sv-rel gl-rel vk-rel mingl-rel m-rel d3d-rel qcc-rel qccgui-scintilla qccgui-dbg gl-dbg sv-dbg plugins-dbg plugins-rel fi +if [ "$BUILD_MSVC" != "n" ]; then + NATIVE_PLUGINS="$PLUGINS_WINDOWS" build "Windows MSVC 32-bit" msvc FTE_TARGET=vc BITS=32 CFLAGS="$WARNINGLEVEL" sv-rel gl-rel vk-rel mingl-rel m-rel d3d-rel qcc-rel qccgui-scintilla qccgui-dbg gl-dbg sv-dbg plugins-dbg plugins-rel + NATIVE_PLUGINS="$PLUGINS_WINDOWS" build "Windows MSVC 64-bit" msvc FTE_TARGET=vc BITS=64 CFLAGS="$WARNINGLEVEL" sv-rel gl-rel vk-rel mingl-rel m-rel d3d-rel qcc-rel qccgui-scintilla qccgui-dbg gl-dbg sv-dbg plugins-dbg plugins-rel +fi export NATIVE_PLUGINS="qi ezhud xmpp irc" if [ "$BUILD_ANDROID" != "n" ]; then build "Android" android droid-rel @@ -175,6 +180,7 @@ fi ####build "MorphOS" morphos CFLAGS="-I$BASE/morphos/os-include/ -I$BASE/morphos/lib/ -L$BASE/morphos/lib/ -I$BASE/zlib/zlib-1.2.5 -L$BASE/zlib/zlib-1.2.5 -I./libs $WARNINGLEVEL" gl-rel mingl-rel sv-rel qcc-rel if [ "$BUILD_MAC" != "n" ]; then #build "MacOSX" macosx_tiger CFLAGS="-I$BASE/mac/x86/include/ -L$BASE/mac/x86/lib -I./libs" FTE_TARGET=macosx_x86 sv-rel gl-rel mingl-rel qcc-rel + #FIXME: figure out how to do universal binaries or whatever they're called build "MacOSX 32-bit" osx32 CC=o32-clang CXX=o32-clang++ FTE_TARGET=osx_x86 BITS=32 sv-rel gl-rel mingl-rel qcc-rel build "MacOSX 64-bit" osx64 CC=o64-clang CXX=o64-clang++ FTE_TARGET=osx_x86_64 BITS=64 sv-rel gl-rel mingl-rel qcc-rel fi @@ -208,7 +214,7 @@ then mv ~/.fte/fte/src/csqcsysdefs.qc $QCCBUILDFOLDER mv ~/.fte/fte/src/menusysdefs.qc $QCCBUILDFOLDER else - echo "Skipping FTE Extensions, no Linux build located" + echo "Skipping FTE Extensions, no Linux gl32 build located" fi diff --git a/engine/Makefile b/engine/Makefile index d42e1cc8..32da686d 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -380,7 +380,7 @@ endif #DO_ECHO=@echo $< && DO_ECHO=@ -DO_ECHO= +#DO_ECHO= DO_CC=$(DO_ECHO) $(CC) $(LTO_CC) $(ALL_CFLAGS) -o $@ -c $< ifeq ($(FTE_TARGET),vc) @@ -452,7 +452,17 @@ DEBUG_CFLAGS?=-ggdb -g DEBUG_CFLAGS+=-DDEBUG RELEASE_CFLAGS?=-O3 -ffast-math $(CPUOPTIMIZATIONS) -ARCH?=$(shell $(CC) -dumpmachine) +ifeq ($(FTE_TARGET),vc) + #msvc doesn't do -dumpmachine. + #we might as well get it to reuse the mingw libraries, if only because that makes those libraries easier to compile... + ifeq ($(BITS),64) + ARCH?=x86_64-w64-mingw32 + else + ARCH?=i686-w64-mingw32 + endif +else + ARCH?=$(shell $(CC) -dumpmachine) +endif ARCHLIBS=$(NATIVE_ABSBASE_DIR)/libs-$(ARCH) #incase our compiler doesn't support it (mingw) @@ -1696,7 +1706,7 @@ m-dbg: m-profile: @$(MAKE) m-tmp TYPE=_clsv-profile OUT_DIR="$(PROFILE_DIR)/$(NCDIRPREFIX)$(MB_DIR)" -.PHONY: m-tmp mcl-tmp mingl-tmp glcl-tmp gl-tmp sv-tmp _clsv-dbg _clsv-rel _cl-dbg _cl-rel _out-rel _out-dbg reldir debugdir makelibs +.PHONY: m-tmp mcl-tmp mingl-tmp glcl-tmp gl-tmp sv-tmp _clsv-dbg _clsv-rel _cl-dbg _cl-rel _out-rel _out-dbg reldir debugdir makelibs wel-rel web-dbg _qcc-tmp: $(REQDIR) @@ -1849,7 +1859,9 @@ nacl-dbg: ################################################# #webgl helpers -ifeq (,$(EMSDK)) +#EMCC?=/opt/emsdk_portable/emscripten/master/emcc +EMCC?=emcc.bat --em-config $(shell cygpath -m $(USERPROFILE))/.emscripten +ifeq ($(EMSDK),) #just adds some extra paths (WINDOWS HOST ONLY) #assumes you installed the emscripten 1.22.0 sdk to EMSCRIPTENROOT #if you have a different version installed, you will need to fix up the paths yourself (or just use fte_target explicitly yourself). @@ -1860,8 +1872,6 @@ else EMSCRIPTENPATH=$(PATH) endif -#EMCC?=/opt/emsdk_portable/emscripten/master/emcc -EMCC?=emcc.bat --em-config $(shell cygpath -m $(USERPROFILE))/.emscripten web-rel: @PATH="$(EMSCRIPTENPATH)" $(MAKE) gl-rel FTE_TARGET=web CC="$(EMCC)" cp $(BASE_DIR)/web/fteshell.html $(RELEASE_DIR)/ftewebgl.html @@ -1871,7 +1881,7 @@ web-rel: web-dbg: @PATH="$(EMSCRIPTENPATH)" $(MAKE) gl-dbg FTE_TARGET=web CC="$(EMCC)" - cp $(BASE_DIR)/web/fteshell.html $(RELEASE_DIR)/ftewebgl.html + cp $(BASE_DIR)/web/fteshell.html $(DEBUG_DIR)/ftewebgl.html @gzip -f $(DEBUG_DIR)/ftewebgl.html @gzip -f $(DEBUG_DIR)/ftewebgl.js @gzip -f $(DEBUG_DIR)/ftewebgl.js.map diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 09a433b2..65f60592 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -2580,7 +2580,6 @@ void CL_QTVPoll (void) //now put it on a menu if (!sourcesmenu) { - m_state = m_complex; Key_Dest_Add(kdm_emenu); sourcesmenu = M_CreateMenu(0); diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 9571a626..538a1ba6 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -3506,7 +3506,7 @@ qboolean CL_AllowArbitaryDownload(char *oldname, char *localfile) if (!Q_strncasecmp(localfile, "game", 4) || //q2-ey things !Q_strcasecmp(localfile, "progs.dat") || !Q_strcasecmp(localfile, "menu.dat") || !Q_strcasecmp(localfile, "csprogs.dat") || !Q_strcasecmp(localfile, "qwprogs.dat") || //overriding gamecode is bad (csqc should be dlcached) strstr(localfile, "\\") || strstr(localfile, "..") || strstr(localfile, "./") || strstr(localfile, ":") || strstr(localfile, "//") || //certain path patterns are just bad - Q_strcasestr(localfile, ".qvm") || Q_strcasestr(localfile, ".dll") || Q_strcasestr(localfile, ".so")) //disallow any native code + Q_strcasestr(localfile, ".qvm") || Q_strcasestr(localfile, ".dll") || Q_strcasestr(localfile, ".so") || Q_strcasestr(localfile, ".dylib")) //disallow any native code { //yes, I know the user can use a different progs from the one that is specified. If you leave it blank there will be no problem. (server isn't allowed to stuff progs cvar) Con_Printf("Ignoring arbitary download to \"%s\" due to possible security risk\n", localfile); return false; @@ -5469,15 +5469,15 @@ void CL_StartCinematicOrMenu(void) if (!sv.state) #endif { - if (qrenderer > QR_NONE && !m_state) + if (qrenderer > QR_NONE && !Key_Dest_Has(kdm_emenu)) { #ifndef NOBUILTINMENUS - if (!cls.state && !m_state && !*FS_GetGamedir(false)) + if (!cls.state && !Key_Dest_Has(kdm_emenu) && !*FS_GetGamedir(false)) M_Menu_Mods_f(); #endif - if (!cls.state && !m_state && cl_demoreel.ival) + if (!cls.state && !Key_Dest_Has(kdm_emenu) && cl_demoreel.ival) CL_NextDemo(); - if (!cls.state && !m_state) + if (!cls.state && !Key_Dest_Has(kdm_emenu)) //if we're (now) meant to be using csqc for menus, make sure that its running. if (!CSQC_UnconnectedInit()) M_ToggleMenu_f(); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 839e25c0..706f91e2 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -2026,8 +2026,6 @@ void DL_Completed(qdownload_t *dl, qofs_t start, qofs_t end) } } -qboolean CL_AllowArbitaryDownload(char *localfile); - static float chunkrate; void CL_ParseChunkedDownload(qdownload_t *dl) @@ -2071,7 +2069,7 @@ void CL_ParseChunkedDownload(qdownload_t *dl) { if (flag == DLERR_REDIRECTFILE) { - if (CL_AllowArbitaryDownload(svname)) + if (CL_AllowArbitaryDownload(dl->remotename, svname)) { Con_Printf("Download of \"%s\" redirected to \"%s\"\n", dl->remotename, svname); if (!strncmp(svname, "package/", 8)) @@ -2195,7 +2193,7 @@ void CL_ParseChunkedDownload(qdownload_t *dl) chunkrate += 1; } -int CL_CountQueuedDownloads(void) +static int CL_CountQueuedDownloads(void) { int count = 0; downloadlist_t *dl; diff --git a/engine/client/cl_plugin.inc b/engine/client/cl_plugin.inc index 4fd9001d..8350f6eb 100644 --- a/engine/client/cl_plugin.inc +++ b/engine/client/cl_plugin.inc @@ -6,7 +6,7 @@ -static plugin_t *menuplug; //plugin that has the current menu +plugin_t *menuplug; //plugin that has the current menu static plugin_t *protocolclientplugin; @@ -27,17 +27,15 @@ static qintptr_t VARGS Plug_Menu_Control(void *offset, quintptr_t mask, const qi Plug_Menu_Event(3, 0); menuplug = NULL; currentplug = oldplug; - Key_Dest_Remove(kdm_emenu); } if (VM_LONG(arg[0]) != 1) return 1; //give us menu control menuplug = currentplug; Key_Dest_Add(kdm_emenu); - m_state = m_plugin; return 1; case 2: //weather it's us or not. - return currentplug == menuplug && m_state == m_plugin; + return currentplug == menuplug && Key_Dest_Has(kdm_emenu); case 3: //weather a menu is active return !!Key_Dest_Has(kdm_emenu|kdm_gmenu); default: diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index 98020bdb..7aea00e1 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -1673,6 +1673,7 @@ void SCR_SetLoadingStage(int stage) if (loadingfile) Z_Free(loadingfile); loadingfile = NULL; + scr_disabled_for_loading = scr_drawloading = false; break; case LS_CONNECTION: SCR_SetLoadingFile("waiting for connection..."); @@ -2030,7 +2031,7 @@ void SCR_SetUpToDrawConsole (void) { if (CL_TryingToConnect()) //if we're trying to connect, make sure there's a loading/connecting screen showing instead of forcing the menu visible SCR_SetLoadingStage(LS_CONNECTION); - else if (!m_state && !startuppending) //don't force anything until the startup stuff has been done + else if (!Key_Dest_Has(kdm_emenu) && !startuppending) //don't force anything until the startup stuff has been done M_ToggleMenu_f(); } } diff --git a/engine/client/client.h b/engine/client/client.h index 960a4841..3e63c177 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -416,6 +416,7 @@ enum qdlabort qboolean DL_Begun(qdownload_t *dl); void DL_Completed(qdownload_t *dl, qofs_t start, qofs_t end); //notifies the download logic that a chunk of the file is no longer needed. void DL_Abort(qdownload_t *dl, enum qdlabort aborttype); //just frees the download's resources. does not delete the temp file. +qboolean CL_AllowArbitaryDownload(char *oldname, char *localfile); //chunked downloads void DLC_Poll(qdownload_t *dl); @@ -1370,7 +1371,7 @@ qboolean CSQC_DrawView(void); qboolean CSQC_UseGamecodeLoadingScreen(void); void CSQC_Shutdown(void); qboolean CSQC_StuffCmd(int lplayernum, char *cmd, char *cmdend); -void CSQC_MapEntityEdited(int idx, const char *newe); +void CSQC_MapEntityEdited(int modelindex, int idx, const char *newe); qboolean CSQC_LoadResource(char *resname, char *restype); qboolean CSQC_ParsePrint(char *message, int printlevel); qboolean CSQC_ParseGamePacket(void); diff --git a/engine/client/image.c b/engine/client/image.c index d512bbe5..599e3b43 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -180,6 +180,7 @@ char *ReadGreyTargaFile (qbyte *data, int flen, tgaheader_t *tgahead, int asgrey return pixels; } +#define MISSHORT(ptr) (*(ptr) | (*(ptr+1) << 8)) //remember to free it qbyte *ReadTargaFile(qbyte *buf, int length, int *width, int *height, qboolean *hasalpha, int asgrey) { @@ -196,8 +197,8 @@ qbyte *ReadTargaFile(qbyte *buf, int length, int *width, int *height, qboolean * tgaheader.id_len = buf[0]; tgaheader.cm_type = buf[1]; tgaheader.version = buf[2]; - tgaheader.cm_idx = LittleShort(*(short *)&buf[3]); - tgaheader.cm_len = LittleShort(*(short *)&buf[5]); + tgaheader.cm_idx = MISSHORT(buf+3); + tgaheader.cm_len = MISSHORT(buf+5); tgaheader.cm_size = buf[7]; tgaheader.originx = LittleShort(*(short *)&buf[8]); tgaheader.originy = LittleShort(*(short *)&buf[10]); @@ -331,7 +332,7 @@ qbyte *ReadTargaFile(qbyte *buf, int length, int *width, int *height, qboolean * packetHeader=*data++; packetSize = 1 + (packetHeader & 0x7f); if (packetHeader & 0x80) - { // run-length packet + { // run-length packet switch (tgaheader.bpp) { case 8: //we made sure this was version 11 @@ -5162,9 +5163,12 @@ void Image_List_f(void) size_t mem = 0; unsigned int loadflags; char fname[MAX_QPATH]; + const char *filter = Cmd_Argv(1); for (tex = imagelist; tex; tex = tex->next) { total++; + if (*filter && !strstr(tex->ident, filter)) + continue; if (tex->subpath) Con_Printf("^h(%s)^h", tex->subpath); diff --git a/engine/client/keys.c b/engine/client/keys.c index 8a56fcc8..0257ee48 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -2290,10 +2290,7 @@ qboolean Key_MouseShouldBeFree(void) // return true; if (Key_Dest_Has(kdm_emenu)) - { - if (m_state == m_complex || m_state == m_plugin /*|| m_state == m_menu_dat*/) - return true; - } + return true; #ifdef VM_UI if (UI_MenuState()) diff --git a/engine/client/m_download.c b/engine/client/m_download.c index c9b6c789..3e65aff1 100644 --- a/engine/client/m_download.c +++ b/engine/client/m_download.c @@ -87,6 +87,8 @@ extern cvar_t pm_downloads_url; #define DPF_ENGINE 0x1000 //engine update. replaces old autoupdate mechanism #define DPF_PLUGIN 0x2000 //this is a plugin package, with a dll +#define DPF_TRUSTED 0x4000 //flag used when parsing package lists. if not set then packages will be ignored if they are anything but paks/pk3s + #define DPF_PRESENT (DPF_NATIVE|DPF_CACHED) #define DPF_DISABLEDINSTALLED (DPF_ENGINE|DPF_PLUGIN) //engines+plugins can be installed without being enabled. //pak.lst @@ -185,6 +187,7 @@ static char *manifestpackages; //metapackage named by the manicfest. static char *declinedpackages; //metapackage named by the manicfest. static int domanifestinstall; //SECURITY_MANIFEST_* +static qboolean pluginpromptshown; //so we only show prompts for new externally-installed plugins once, instead of every time the file is reloaded. static qboolean doautoupdate; //updates will be marked (but not applied without the user's actions) static qboolean pkg_updating; //when flagged, further changes are blocked until completion. @@ -194,6 +197,7 @@ static struct { char *url; char *prefix; + qboolean trustworthy; //trusted char received; //says if we got a response yet or not qboolean save; //written into our local file struct dl_download *curdl; //the download context @@ -559,7 +563,7 @@ static void PM_AddDep(package_t *p, int deptype, const char *depname) *link = nd; } -static void PM_AddSubList(const char *url, const char *prefix, qboolean save) +static void PM_AddSubList(const char *url, const char *prefix, qboolean save, qboolean trustworthy) { int i; if (!*url) @@ -576,6 +580,10 @@ static void PM_AddSubList(const char *url, const char *prefix, qboolean save) } if (i == numdownloadablelists && i < countof(downloadablelist)) { + if (!strncmp(url, "https:", 6)) + downloadablelist[i].trustworthy = trustworthy; + else + downloadablelist[i].trustworthy = false; //if its not a secure url, never consider it as trustworthy downloadablelist[i].save = save; downloadablelist[i].url = BZ_Malloc(strlen(url)+1); @@ -665,7 +673,7 @@ static void PM_ParsePackageList(vfsfile_t *f, int parseflags, const char *url, c subprefix = va("%s/%s", prefix, Cmd_Argv(2)); else subprefix = Cmd_Argv(2); - PM_AddSubList(Cmd_Argv(1), subprefix, (parseflags & DPF_ENABLED)?true:false); + PM_AddSubList(Cmd_Argv(1), subprefix, (parseflags & DPF_ENABLED)?true:false, (parseflags&DPF_TRUSTED)); continue; } if (!strcmp(Cmd_Argv(0), "set")) @@ -729,6 +737,7 @@ static void PM_ParsePackageList(vfsfile_t *f, int parseflags, const char *url, c int extract = EXTRACT_COPY; int priority = PM_DEFAULTPRIORITY; unsigned int flags = parseflags; + enum fs_relative fsroot = FS_ROOT; int i; if (version > 2) @@ -795,6 +804,13 @@ static void PM_ParsePackageList(vfsfile_t *f, int parseflags, const char *url, c flags &= ~DPF_ENABLED; //known about, (probably) cached, but not actually enabled. else if (!strncmp(arg, "installed=", 6) && version>2) flags |= parseflags & DPF_ENABLED; + else if (!strncmp(arg, "root=", 5) && (parseflags&DPF_ENABLED)) + { + if (!Q_strcasecmp(arg+5, "bin")) + fsroot = FS_BINARYPATH; + else + fsroot = FS_ROOT; + } else { Con_DPrintf("Unknown package property\n"); @@ -836,7 +852,7 @@ static void PM_ParsePackageList(vfsfile_t *f, int parseflags, const char *url, c Q_strncpyz(p->version, ver?ver:"", sizeof(p->version)); Q_snprintfz(p->gamedir, sizeof(p->gamedir), "%s", gamedir); - p->fsroot = FS_ROOT; + p->fsroot = fsroot; p->extract = extract; p->priority = priority; p->flags = flags; @@ -927,7 +943,7 @@ static void PM_ParsePackageList(vfsfile_t *f, int parseflags, const char *url, c } else if (!Q_strcasecmp(p->arch, THISARCH)) { - if (p->fsroot == FS_ROOT && !*p->gamedir) + if ((p->fsroot == FS_ROOT || p->fsroot == FS_BINARYPATH) && !*p->gamedir) p->flags |= DPF_PLUGIN; } else @@ -976,6 +992,109 @@ void PM_EnumeratePlugins(void (*callback)(const char *name)) } #endif +#ifdef PLUGINS +static void PM_WriteInstalledPackages(void); +static package_t *PM_FindPackage(const char *packagename); +static int QDECL PM_EnumeratedPlugin (const char *name, qofs_t size, time_t mtime, void *param, searchpathfuncs_t *spath) +{ + package_t *p; + struct packagedep_s *dep; + char vmname[MAX_QPATH]; + int len; + char *dot; + if (!strncmp(name, "fteplug_", 8)) + Q_strncpyz(vmname, name+8, sizeof(vmname)); + else + Q_strncpyz(vmname, name, sizeof(vmname)); + len = strlen(vmname); + len -= strlen(ARCH_CPU_POSTFIX ARCH_DL_POSTFIX); + if (!strcmp(vmname+len, ARCH_CPU_POSTFIX ARCH_DL_POSTFIX)) + vmname[len] = 0; + else + { + dot = strchr(vmname, '.'); + if (dot) + *dot = 0; + } + len = strlen(vmname); + if (len > 0 && vmname[len-1] == '_') + vmname[len-1] = 0; + + for (p = availablepackages; p; p = p->next) + { + if (!(p->flags & DPF_PLUGIN)) + continue; + for (dep = p->deps; dep; dep = dep->next) + { + if (dep->dtype != DEP_FILE) + continue; + if (!Q_strcasecmp(dep->name, name)) + return true; + } + } + + if (PM_FindPackage(vmname)) + return true; //don't include it if its a dupe anyway. + + p = Z_Malloc(sizeof(*p)); + p->deps = Z_Malloc(sizeof(*p->deps) + strlen(name)); + p->deps->dtype = DEP_FILE; + strcpy(p->deps->name, name); + p->arch = Z_StrDup(THISARCH); + p->name = Z_StrDup(vmname); + p->title = Z_StrDup(vmname); + p->category = Z_StrDup("Plugins/"); + p->priority = PM_DEFAULTPRIORITY; + p->fsroot = FS_BINARYPATH; + strcpy(p->version, "??""??"); + p->flags = DPF_PLUGIN|DPF_NATIVE|DPF_FORGETONUNINSTALL; + PM_InsertPackage(p); + + *(int*)param = true; + + return true; +} +void PM_PluginDetected(void *ctx, int status) +{ + PM_WriteInstalledPackages(); + if (status == 0) + { + Cmd_ExecuteString("menu_download\n", RESTRICT_LOCAL); + Cmd_ExecuteString("menu_download \"Plugins/\"\n", RESTRICT_LOCAL); + } +} +#endif + +static void PM_PreparePackageList(void) +{ + //figure out what we've previously installed. + if (!loadedinstalled) + { + char nat[MAX_OSPATH]; + vfsfile_t *f = FS_OpenVFS(INSTALLEDFILES, "rb", FS_ROOT); + loadedinstalled = true; + if (f) + { + PM_ParsePackageList(f, DPF_FORGETONUNINSTALL|DPF_ENABLED, NULL, ""); + VFS_CLOSE(f); + } + +#ifdef PLUGINS + { + int foundone = false; + FS_NativePath("", FS_BINARYPATH, nat, sizeof(nat)); + Con_DPrintf("Loading plugins from \"%s\"\n", nat); + Sys_EnumerateFiles(nat, "fteplug_*" ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, PM_EnumeratedPlugin, &foundone, NULL); + if (foundone && !pluginpromptshown) + { + pluginpromptshown = true; + M_Menu_Prompt(PM_PluginDetected, NULL, "Plugin(s) appears to have\nbeen installed externally.\nUse the updates menu\ntoenable them.", "View", NULL, "Disable"); + } + } +#endif + } +} + void PM_LoadPackages(searchpath_t **oldpaths, const char *parent_pure, const char *parent_logical, searchpath_t *search, unsigned int loadstuff, int minpri, int maxpri) { package_t *p; @@ -984,16 +1103,7 @@ void PM_LoadPackages(searchpath_t **oldpaths, const char *parent_pure, const cha int pri; //figure out what we've previously installed. - if (!loadedinstalled) - { - vfsfile_t *f = FS_OpenVFS(INSTALLEDFILES, "rb", FS_ROOT); - loadedinstalled = true; - if (f) - { - PM_ParsePackageList(f, DPF_FORGETONUNINSTALL|DPF_ENABLED, NULL, ""); - VFS_CLOSE(f); - } - } + PM_PreparePackageList(); do { @@ -1051,24 +1161,6 @@ void PM_Shutdown(void) loadedinstalled = false; } - -static void PM_PreparePackageList(void) -{ - //figure out what we've previously installed. - if (!loadedinstalled) - { - vfsfile_t *f = FS_OpenVFS(INSTALLEDFILES, "rb", FS_ROOT); - loadedinstalled = true; - if (f) - { - PM_ParsePackageList(f, DPF_FORGETONUNINSTALL|DPF_ENABLED, NULL, ""); - VFS_CLOSE(f); - } - } -} - - - //finds the newest version static package_t *PM_FindPackage(const char *packagename) { @@ -1484,7 +1576,6 @@ static void PM_ListDownloaded(struct dl_download *dl) if (Key_Dest_Has(kdm_emenu)) { Key_Dest_Remove(kdm_emenu); - m_state = m_none; } #ifdef MENU_DAT if (Key_Dest_Has(kdm_gmenu)) @@ -1507,10 +1598,7 @@ static void PM_ListDownloaded(struct dl_download *dl) if (!isDedicated) { if (Key_Dest_Has(kdm_emenu)) - { Key_Dest_Remove(kdm_emenu); - m_state = m_none; - } #ifdef MENU_DAT if (Key_Dest_Has(kdm_gmenu)) MP_Toggle(0); @@ -1535,7 +1623,7 @@ static void PM_UpdatePackageList(qboolean autoupdate, int retry) //make sure our sources are okay. if (*pm_downloads_url.string) - PM_AddSubList(pm_downloads_url.string, "", true); + PM_AddSubList(pm_downloads_url.string, "", true, true); doautoupdate |= autoupdate; @@ -1702,6 +1790,12 @@ static void PM_WriteInstalledPackages(void) COM_QuotedConcat(va("preview=%s", p->previewimage), buf, sizeof(buf)); } + if (p->fsroot == FS_BINARYPATH) + { + Q_strncatz(buf, " ", sizeof(buf)); + COM_QuotedConcat(va("root=bin", p->previewimage), buf, sizeof(buf)); + } + for (dep = p->deps; dep; dep = dep->next) { if (dep->dtype == DEP_FILE) @@ -1791,7 +1885,7 @@ static int QDECL PM_ExtractFiles(const char *fname, qofs_t fsize, time_t mtime, //package has been downloaded and installed, but some packages need to be enabled //(plugins might have other dll dependancies, so this can only happen AFTER the entire package was extracted) -void PM_PackageEnabled(package_t *p) +static void PM_PackageEnabled(package_t *p) { char ext[8]; struct packagedep_s *dep; @@ -2267,8 +2361,12 @@ static void PM_ApplyChanges(void) if (reloadpacks) FS_ReloadPackFiles(); - if (p->flags & DPF_FORGETONUNINSTALL) + if ((p->flags & DPF_FORGETONUNINSTALL) && !(p->flags & DPF_PRESENT)) { +#if 1 + downloadablessequence++; + PM_FreePackage(p); +#else if (p->alternative) { //replace it with its alternative package *p->link = p->alternative; @@ -2287,6 +2385,7 @@ static void PM_ApplyChanges(void) //FIXME: the menu(s) hold references to packages, so its not safe to purge them p->flags |= DPF_HIDDEN; // BZ_Free(p); +#endif continue; } @@ -2569,7 +2668,7 @@ void PM_Command_f(void) Con_Printf("<%i sources>\n", numdownloadablelists); } else - PM_AddSubList(Cmd_Argv(2), "", true); + PM_AddSubList(Cmd_Argv(2), "", true, true); } else if (!strcmp(act, "remsource")) PM_RemSubList(Cmd_Argv(2)); @@ -2676,16 +2775,7 @@ qboolean PM_FindUpdatedEngine(char *syspath, size_t syspathsize) package_t *e = NULL, *p; char *pfname; //figure out what we've previously installed. - if (!loadedinstalled) - { - vfsfile_t *f = FS_OpenVFS(INSTALLEDFILES, "rb", FS_ROOT); - loadedinstalled = true; - if (f) - { - PM_ParsePackageList(f, DPF_FORGETONUNINSTALL|DPF_ENABLED, NULL, ""); - VFS_CLOSE(f); - } - } + PM_PreparePackageList(); for (p = availablepackages; p; p = p->next) { @@ -2998,7 +3088,7 @@ static qboolean MD_RevertUpdates (union menuoption_s *mo,struct menu_s *m,int ke return false; } -void M_AddItemsToDownloadMenu(menu_t *m) +static void MD_AddItemsToDownloadMenu(menu_t *m) { char path[MAX_QPATH]; int y; @@ -3094,7 +3184,7 @@ void M_AddItemsToDownloadMenu(menu_t *m) } #include "shader.h" -void M_Download_UpdateStatus(struct menu_s *m) +static void MD_Download_UpdateStatus(struct menu_s *m) { dlmenu_t *info = m->data; int i; @@ -3130,7 +3220,7 @@ void M_Download_UpdateStatus(struct menu_s *m) } info->populated = true; - M_AddItemsToDownloadMenu(m); + MD_AddItemsToDownloadMenu(m); } if (m->selecteditem && m->selecteditem->common.type == mt_custom && m->selecteditem->custom.dptr) @@ -3151,13 +3241,12 @@ void Menu_DownloadStuff_f (void) dlmenu_t *info; Key_Dest_Add(kdm_emenu); - m_state = m_complex; menu = M_CreateMenu(sizeof(dlmenu_t)); info = menu->data; menu->persist = true; - menu->predraw = M_Download_UpdateStatus; + menu->predraw = MD_Download_UpdateStatus; info->downloadablessequence = downloadablessequence; diff --git a/engine/client/m_items.c b/engine/client/m_items.c index d7d2b4b1..146c32ca 100644 --- a/engine/client/m_items.c +++ b/engine/client/m_items.c @@ -1510,7 +1510,6 @@ changed: void M_AddMenu (menu_t *menu) { - m_state = m_complex; menu->prev = topmenu; if (topmenu) topmenu->next = menu; @@ -1649,7 +1648,6 @@ void M_Complex_Draw(void) if (!topmenu) { Key_Dest_Remove(kdm_emenu); - m_state = m_none; return; } @@ -1927,7 +1925,6 @@ qboolean MC_Main_Key (int key, menu_t *menu) //here purly to restart demos. return true; Key_Dest_Remove(kdm_emenu); - m_state = m_none; return true; } return false; @@ -1986,7 +1983,6 @@ void M_Menu_Main_f (void) { if (R_GetShaderSizes(R2D_SafeCachePic("pics/m_main_quit"), NULL, NULL, true) > 0) { - m_state = m_complex; Key_Dest_Add(kdm_emenu); mainm = M_CreateMenu(0); @@ -2044,7 +2040,6 @@ void M_Menu_Main_f (void) if (R_GetShaderSizes(p, NULL, NULL, true) <= 0) return; - m_state = m_complex; Key_Dest_Add(kdm_emenu); mainm = M_CreateMenu(0); mainm->key = MC_Main_Key; @@ -2084,7 +2079,6 @@ void M_Menu_Main_f (void) if (QBigFontWorks()) { int y; - m_state = m_complex; Key_Dest_Add(kdm_emenu); mainm = M_CreateMenu(0); @@ -2136,7 +2130,6 @@ void M_Menu_Main_f (void) else { int width; - m_state = m_complex; Key_Dest_Add(kdm_emenu); mainm = M_CreateMenu(0); diff --git a/engine/client/m_master.c b/engine/client/m_master.c index 33d40cfd..7d20f8c6 100644 --- a/engine/client/m_master.c +++ b/engine/client/m_master.c @@ -1134,7 +1134,6 @@ void M_Menu_ServerList2_f(void) Key_Dest_Remove(kdm_console); Key_Dest_Add(kdm_emenu); - m_state = m_complex; menu = M_CreateMenu(sizeof(serverlist_t)); menu->predraw = SL_PreDraw; @@ -1335,7 +1334,6 @@ void M_QuickConnect_f(void) menu_t *menu; Key_Dest_Add(kdm_emenu); - m_state = m_complex; MasterInfo_Refresh(); isrefreshing = true; diff --git a/engine/client/m_mp3.c b/engine/client/m_mp3.c index 740403e3..5f56d589 100644 --- a/engine/client/m_mp3.c +++ b/engine/client/m_mp3.c @@ -1260,7 +1260,6 @@ qboolean M_Media_Key (int key, menu_t *menu) void M_Menu_Media_f (void) { menu_t *menu; - m_state = m_complex; Key_Dest_Add(kdm_emenu); menu = M_CreateMenu(0); @@ -2481,10 +2480,7 @@ qboolean Media_PlayFilm(char *name, qboolean enqueue) SCR_EndLoadingPlaque(); if (Key_Dest_Has(kdm_emenu)) - { Key_Dest_Remove(kdm_emenu); - m_state = m_none; - } #ifdef MENU_DAT if (Key_Dest_Has(kdm_gmenu)) MP_Toggle(0); diff --git a/engine/client/m_multi.c b/engine/client/m_multi.c index 423f50ae..3e307888 100644 --- a/engine/client/m_multi.c +++ b/engine/client/m_multi.c @@ -19,7 +19,6 @@ void M_Menu_MultiPlayer_f (void) p = NULL; Key_Dest_Add(kdm_emenu); - m_state = m_complex; mgt = M_GameType(); @@ -442,7 +441,6 @@ void M_Menu_Setup_f (void) }; mpic_t *p; menucustom_t *cu; - m_state = m_complex; Key_Dest_Add(kdm_emenu); menu = M_CreateMenu(sizeof(setupmenu_t)); @@ -470,7 +468,6 @@ void M_Menu_Setup_f (void) #endif Key_Dest_Add(kdm_emenu); - m_state = m_complex; menu = M_CreateMenu(sizeof(setupmenu_t)); info = menu->data; @@ -657,7 +654,6 @@ void M_Menu_GameOptions_f (void) int players; Key_Dest_Add(kdm_emenu); - m_state = m_complex; menu = M_CreateMenu(sizeof(newmultimenu_t)); info = menu->data; diff --git a/engine/client/m_options.c b/engine/client/m_options.c index 8c36bf5f..7e513100 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -104,7 +104,6 @@ menu_t *M_Options_Title(int *y, int infosize) *y = 32; Key_Dest_Add(kdm_emenu); - m_state = m_complex; menu = M_CreateMenu(infosize); @@ -401,7 +400,6 @@ void M_Menu_Audio_Speakers_f (void) menu_t *menu; Key_Dest_Add(kdm_emenu); - m_state = m_complex; menu = M_CreateMenu(sizeof(audiomenuinfo_t)); info = menu->data; @@ -3004,7 +3002,7 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_ mods->fixedrate = 1; while (mods->fixedrate >= rate) { - sv.world.rbe->Frame(&mods->ragworld, rate, 800); + sv.world.rbe->RunFrame(&mods->ragworld, rate, 800); mods->fixedrate -= rate; } @@ -3511,7 +3509,13 @@ static void Mods_Draw(int x, int y, struct menucustom_s *c, struct menu_s *m) if (!mods->nummanifests) { Draw_FunString(x, y+0, "No games or mods known"); - Draw_FunString(x, y+8, "You may need to use -basedir $PATHTOGAME on the commandline"); +#if defined(FTE_TARGET_WEB) || defined(NACL) + Draw_FunString(x, y+8, "Connection issue or bad server config"); +#else + Draw_FunString(x, y+8, "You may need to use"); + Draw_FunString(x, y+16, " -basedir $PATHTOGAME"); + Draw_FunString(x, y+24, " on the commandline"); +#endif return; } diff --git a/engine/client/m_script.c b/engine/client/m_script.c index 089cb8ba..4fbc6809 100644 --- a/engine/client/m_script.c +++ b/engine/client/m_script.c @@ -94,7 +94,6 @@ void M_MenuS_Script_f (void) //create a menu. menu_t *oldmenu; char *alias = Cmd_Argv(1); Key_Dest_Add(kdm_emenu); - m_state = m_complex; selectitem = 0; items=0; diff --git a/engine/client/m_single.c b/engine/client/m_single.c index 2e4bde95..e63c2ef5 100644 --- a/engine/client/m_single.c +++ b/engine/client/m_single.c @@ -231,7 +231,6 @@ void M_Menu_Save_f (void) return; Key_Dest_Add(kdm_emenu); - m_state = m_complex; menu = M_CreateMenu(sizeof(loadsavemenuinfo_t)); menu->data = menu+1; @@ -263,7 +262,6 @@ void M_Menu_Load_f (void) char time[64]; Key_Dest_Add(kdm_emenu); - m_state = m_complex; menu = M_CreateMenu(sizeof(loadsavemenuinfo_t)); menu->data = menu+1; @@ -308,7 +306,6 @@ void M_Menu_SinglePlayer_f (void) #endif Key_Dest_Add(kdm_emenu); - m_state = m_complex; #ifdef CLIENTONLY menu = M_CreateMenu(0); @@ -978,7 +975,6 @@ void M_Menu_Demos_f (void) Key_Dest_Add(kdm_emenu); Key_Dest_Remove(kdm_console); - m_state = m_complex; menu = M_CreateMenu(sizeof(demomenu_t)); menu->remove = M_Demo_Remove; @@ -1046,7 +1042,6 @@ void M_Menu_MediaFiles_f (void) static demoloc_t mediareenterloc = {FS_GAME}; Key_Dest_Add(kdm_emenu); - m_state = m_complex; menu = M_CreateMenu(sizeof(demomenu_t)); menu->remove = M_Demo_Remove; diff --git a/engine/client/menu.c b/engine/client/menu.c index b3a14c10..a6e53539 100644 --- a/engine/client/menu.c +++ b/engine/client/menu.c @@ -21,7 +21,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "winquake.h" #include "shader.h" -m_state_t m_state; qboolean menu_mousedown; void M_DrawScalePic (int x, int y, int w, int h, mpic_t *pic) @@ -317,7 +316,6 @@ void M_CloseMenu_f (void) return; M_RemoveAllMenus(false); Key_Dest_Remove(kdm_emenu); - m_state = m_none; } /* ================ @@ -326,7 +324,7 @@ M_ToggleMenu_f */ void M_ToggleMenu_f (void) { - if (m_state) + if (topmenu) { Key_Dest_Add(kdm_emenu); return; @@ -355,7 +353,6 @@ void M_ToggleMenu_f (void) if (Key_Dest_Has(kdm_emenu)) { Key_Dest_Remove(kdm_emenu); - m_state = m_none; return; } if (Key_Dest_Has(kdm_console|kdm_cwindows)) @@ -510,7 +507,6 @@ void M_Menu_Keys_f (void) extern cvar_t cl_splitscreen; Key_Dest_Add(kdm_emenu); - m_state = m_complex; menu = M_CreateMenu(0); switch(M_GameType()) @@ -634,11 +630,75 @@ struct {"gfx/menu/help%02i.lmp",1} //hexen2 }; +void M_Help_Draw (menu_t *m) +{ + int i; + mpic_t *pic = NULL; + for (i = 0; i < sizeof(helpstyles)/sizeof(helpstyles[0]) && !pic; i++) + { + pic = R2D_SafeCachePic(va(helpstyles[i].pattern, help_page+helpstyles[i].base)); + if (R_GetShaderSizes(pic, NULL, NULL, true) <= 0) + pic = NULL; + } + if (!pic) + M_Menu_Main_f (); + else + { + //define default aspect ratio + int width = 320; + int height = 200; + + //figure out which axis we're meeting. + if (vid.width/(float)width > vid.height/(float)height) + { + width = width * (vid.height/(float)height); + height = vid.height; + } + else + { + height = height * (vid.width/(float)width); + width = vid.width; + } + R2D_ScalePic ((vid.width-width)/2, (vid.height-height)/2, width, height, pic); + } +} +qboolean M_Help_Key (int key, menu_t *m) +{ + switch (key) + { + case K_ESCAPE: + case K_MOUSE2: + M_Menu_Main_f (); + return true; + + case K_UPARROW: + case K_RIGHTARROW: + case K_MOUSE1: + S_LocalSound ("misc/menu2.wav"); + if (++help_page >= num_help_pages) + help_page = 0; + return true; + + case K_DOWNARROW: + case K_LEFTARROW: + S_LocalSound ("misc/menu2.wav"); + if (--help_page < 0) + help_page = num_help_pages-1; + return true; + default: + return false; + } +} + void M_Menu_Help_f (void) { int i; + menu_t *helpmenu = M_CreateMenu(0); Key_Dest_Add(kdm_emenu); - m_state = m_help; + + helpmenu->predraw = M_Help_Draw; + helpmenu->key = M_Help_Key; + help_page = 0; num_help_pages = 1; @@ -656,52 +716,6 @@ void M_Menu_Help_f (void) } - -void M_Help_Draw (void) -{ - int i; - mpic_t *pic = NULL; - for (i = 0; i < sizeof(helpstyles)/sizeof(helpstyles[0]) && !pic; i++) - { - pic = R2D_SafeCachePic(va(helpstyles[i].pattern, help_page+helpstyles[i].base)); - if (R_GetShaderSizes(pic, NULL, NULL, true) <= 0) - pic = NULL; - } - if (!pic) - M_Menu_Main_f (); - else - M_DrawScalePic (0, 0, 320, 200, pic); -} - - -void M_Help_Key (int key) -{ - switch (key) - { - case K_ESCAPE: - case K_MOUSE2: - M_Menu_Main_f (); - break; - - case K_UPARROW: - case K_RIGHTARROW: - case K_MOUSE1: - S_LocalSound ("misc/menu2.wav"); - if (++help_page >= num_help_pages) - help_page = 0; - break; - - case K_DOWNARROW: - case K_LEFTARROW: - S_LocalSound ("misc/menu2.wav"); - if (--help_page < 0) - help_page = num_help_pages-1; - break; - } - -} - - //============================================================================= /* Various callback-based prompts */ @@ -756,7 +770,6 @@ void M_Menu_Prompt (void (*callback)(void *, int), void *ctx, const char *messag int x = 64, w = 224; Key_Dest_Add(kdm_emenu); - m_state = m_complex; m = (promptmenu_t*)M_CreateMenuInfront(sizeof(*m) - sizeof(m->m) + strlen(messages)+(optionyes?strlen(optionyes):0)+(optionno?strlen(optionno):0)+(optioncancel?strlen(optioncancel):0)+6); m->callback = callback; @@ -900,10 +913,7 @@ void M_Quit_Key (int key) m_entersound = true; } else - { key_dest = key_game; - m_state = m_none; - } break; case 'Y': @@ -1086,7 +1096,6 @@ void M_Menu_Quit_f (void) case 2: Key_Dest_Add(kdm_emenu); Key_Dest_Remove(kdm_console); - m_state = m_complex; quitmenu = M_CreateMenuInfront(0); quitmenu->key = MC_SaveQuit_Key; @@ -1110,7 +1119,6 @@ void M_Menu_Quit_f (void) case 1: Key_Dest_Add(kdm_emenu); Key_Dest_Remove(kdm_console); - m_state = m_complex; quitmenu = M_CreateMenuInfront(0); quitmenu->key = MC_Quit_Key; @@ -1373,6 +1381,9 @@ void M_Init (void) void M_Draw (int uimenu) { + extern menu_t *topmenu; + qboolean stillactive = false; + if (uimenu) { if (uimenu == 2) @@ -1383,27 +1394,18 @@ void M_Draw (int uimenu) } #ifndef NOBUILTINMENUS - if (m_state != m_complex) + if (!Key_Dest_Has(kdm_emenu)) { M_RemoveAllMenus(false); menu_mousedown = false; + return; } #endif - if (!Key_Dest_Has(kdm_emenu)) - { - m_state = m_none; - return; - } - - if (m_state == m_none) - return; - #ifndef NOBUILTINMENUS if ((!menu_script || scr_con_current) && !m_recursiveDraw) { - extern menu_t *topmenu; - if (m_state == m_complex && topmenu && topmenu->selecteditem && topmenu->selecteditem->common.type == mt_slider && (topmenu->selecteditem->slider.var == &v_gamma || topmenu->selecteditem->slider.var == &v_contrast)) + if (topmenu && topmenu->selecteditem && topmenu->selecteditem->common.type == mt_slider && (topmenu->selecteditem->slider.var == &v_gamma || topmenu->selecteditem->slider.var == &v_contrast)) /*no menu tint if we're trying to adjust gamma*/; else R2D_FadeScreen (); @@ -1416,45 +1418,31 @@ void M_Draw (int uimenu) R2D_ImageColours(1, 1, 1, 1); - switch (m_state) +#ifdef PLUGINS + if (menuplug) { - default: - case m_none: - break; + Plug_Menu_Event (0, (int)(realtime*1000)); + stillactive = true; + } +#endif #ifndef NOBUILTINMENUS - case m_help: - M_Help_Draw (); - break; - - case m_complex: + if (topmenu) + { M_Complex_Draw (); - break; -#endif - -#ifdef PLUGINS - case m_plugin: - Plug_Menu_Event (0, (int)(realtime*1000)); - break; -#endif + stillactive = true; } +#endif + if (!stillactive) + Key_Dest_Remove(kdm_emenu); } void M_Keydown (int key, int unicode) { - switch (m_state) - { - default: - case m_none: - Key_Dest_Remove(kdm_emenu); - return; #ifndef NOBUILTINMENUS - case m_help: - M_Help_Key (key); - return; - - case m_complex: + if (topmenu) + { if (key == K_MOUSE1) //mouse clicks are deferred until the release event. this is for touch screens and aiming. menu_mousedown = true; else if (key == K_LSHIFT || key == K_RSHIFT || key == K_LALT || key == K_RALT || key == K_LCTRL || key == K_RCTRL) @@ -1462,38 +1450,41 @@ void M_Keydown (int key, int unicode) else M_Complex_Key (key, unicode); return; + } #endif #ifdef PLUGINS - case m_plugin: + if (menuplug) + { Plug_Menu_Event (1, key); return; -#endif } +#endif + + Key_Dest_Remove(kdm_emenu); } void M_Keyup (int key, int unicode) { - switch (m_state) - { #ifndef NOBUILTINMENUS - case m_complex: + if (topmenu) + { if (key == K_MOUSE1 && menu_mousedown) M_Complex_Key (key, unicode); else if (key == K_LSHIFT || key == K_RSHIFT || key == K_LALT || key == K_RALT || key == K_LCTRL || key == K_RCTRL) M_Complex_Key (key, unicode); menu_mousedown = false; return; + } #endif #ifdef PLUGINS - case m_plugin: + if (menuplug) + { Plug_Menu_Event (2, key); return; -#endif - default: - break; } +#endif } // Generic function to choose which game menu to draw diff --git a/engine/client/menu.h b/engine/client/menu.h index cd40b893..f9391d54 100644 --- a/engine/client/menu.h +++ b/engine/client/menu.h @@ -91,8 +91,12 @@ void M_SomeInitialisationFunctionCalledAtStartup(void) } */ -typedef enum {m_none, m_complex, m_help, m_plugin} m_state_t; -extern m_state_t m_state; +#ifdef PLUGINS +extern struct plugin_s *menuplug; +#endif +#ifndef NOBUILTINMENUS +extern struct menu_s *topmenu; +#endif void M_DrawTextBox (int x, int y, int width, int lines); #ifndef NOBUILTINMENUS diff --git a/engine/client/p_script.c b/engine/client/p_script.c index 08512884..fd0de315 100644 --- a/engine/client/p_script.c +++ b/engine/client/p_script.c @@ -712,6 +712,7 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn) case BM_INVMODA: bmpostfix = "#INVMODA"; break; case BM_INVMODC: bmpostfix = "#INVMODC"; break; case BM_PREMUL: bmpostfix = "#PREMUL"; break; + case BM_RTSMOKE: bmpostfix = "#RTSMOKE"; break; } /*try and load the shader, fail if we would need to generate one*/ ptype->looks.shader = R_RegisterCustom(va("%s%s", ptype->texname, bmpostfix), SUF_NONE, NULL, NULL); @@ -885,6 +886,29 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn) "}\n" ; break; + case BM_RTSMOKE: + namepostfix = "_rts"; + defaultshader = + "{\n" + "program defaultsprite#LIGHT\n" + "{\n" + "map $diffuse\n" + "blendfunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA\n" + "rgbgen const vertex\n" + "alphagen vertex\n" + "}\n" + "surfaceparm noshadows\n" + "sort seethrough\n" //needs to be low enough that its subject to rtlights + "bemode rtlight\n" + "{\n" + "program rtlight#NOBUMP#VERTEXCOLOURS\n" + "{\n" + "map $diffuse\n" + "blendfunc add\n" + "}\n" + "}\n" + "}\n"; + break; } memset(&tn, 0, sizeof(tn)); @@ -945,6 +969,8 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn) } R_BuildDefaultTexnums(&tn, ptype->looks.shader); } + else + R_BuildDefaultTexnums(NULL, ptype->looks.shader); } static void P_ResetToDefaults(part_type_t *ptype) @@ -1836,6 +1862,11 @@ parsefluid: ptype->looks.premul = 1; ptype->looks.blendmode = BM_PREMUL; } + else if (!strcmp(value, "rtsmoke")) + { + ptype->looks.premul = 1; + ptype->looks.blendmode = BM_RTSMOKE; + } else { Con_DPrintf("%s.%s: uses unknown blend type '%s', assuming legacy 'blendalpha'\n", ptype->config, ptype->name, value); @@ -2307,6 +2338,9 @@ qboolean PScript_Query(int typenum, int body, char *outstr, int outstrlen) else Q_strncatz(outstr, "blend premul_blend\n", outstrlen); break; + case BM_RTSMOKE: + Q_strncatz(outstr, "blend rtsmoke\n", outstrlen); + break; } for (i = 0; i < ptype->nummodels; i++) diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 8e75d752..2c5ade96 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -1318,6 +1318,7 @@ static int csqc_poly_2d; #define DRAWFLAG_MODULATE2 3 #define DRAWFLAG_2D (1u<<2) #define DRAWFLAG_TWOSIDED 0x400 +#define DRAWFLAG_LINES 0x800 static void CSQC_PolyFlush(void) { @@ -1542,6 +1543,8 @@ void QCBUILTIN PF_R_AddTrisoup_Simple(pubprogfuncs_t *prinst, struct globalvars_ beflags = BEF_NOSHADOWS; if (qcflags & DRAWFLAG_TWOSIDED) beflags |= BEF_FORCETWOSIDED; + if (qcflags & DRAWFLAG_LINES) + beflags |= BEF_LINES; if (twod) shader = R_RegisterPic(PR_GetStringOfs(prinst, OFS_PARM0), NULL); @@ -7527,7 +7530,7 @@ qboolean CSQC_DrawView(void) #ifdef RAGDOLL rag_doallanimations(&sv.world); #endif - csqc_world.rbe->Frame(&csqc_world, host_frametime, 800); + csqc_world.rbe->RunFrame(&csqc_world, host_frametime, 800); } #endif @@ -7619,7 +7622,7 @@ qboolean CSQC_DrawView(void) void *pr_globals = PR_globals(csqcprogs, PR_CURRENT); G_FLOAT(OFS_PARM0) = vid.width; G_FLOAT(OFS_PARM1) = vid.height; - G_FLOAT(OFS_PARM2) = !m_state && !r_refdef.eyeoffset[0] && !r_refdef.eyeoffset[1]; + G_FLOAT(OFS_PARM2) = !Key_Dest_Has(kdm_emenu) && !r_refdef.eyeoffset[0] && !r_refdef.eyeoffset[1]; } //end EXT_CSQC_1 if (csqcg.f_updateviewloading && cls.state && cls.state < ca_active) @@ -7862,12 +7865,15 @@ qboolean CSQC_ParseGamePacket(void) return true; } -void CSQC_MapEntityEdited(int idx, const char *newe) +void CSQC_MapEntityEdited(int modelindex, int idx, const char *newe) { void *pr_globals; if (!csqcprogs || !csqcg.mapentityedited) return; + if (modelindex != 1) + return; + pr_globals = PR_globals(csqcprogs, PR_CURRENT); G_INT(OFS_PARM0) = idx; (((string_t *)pr_globals)[OFS_PARM1] = PR_TempString(csqcprogs, newe)); diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index df8e8724..51fe2a8d 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -21,6 +21,7 @@ extern unsigned int r2d_be_flags; #define DRAWFLAG_MODULATE2 3 #define DRAWFLAG_2D (1u<<2) #define DRAWFLAG_TWOSIDED 0x400 +#define DRAWFLAG_LINES 0x800 static unsigned int PF_SelectDPDrawFlag(int flag) { csqc_dp_lastwas3d = false; //for compat with dp's stupid beginpolygon diff --git a/engine/client/renderer.c b/engine/client/renderer.c index 1bf5b9ee..5800e1c6 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -529,7 +529,7 @@ void R_InitTextures (void) { int x,y, m; qbyte *dest; - static char r_notexture_mip_mem[(sizeof(texture_t) + 16*16)]; + static FTE_ALIGN(4) char r_notexture_mip_mem[(sizeof(texture_t) + 16*16)]; // create a simple checkerboard texture for the default r_notexture_mip = (texture_t*)r_notexture_mip_mem; diff --git a/engine/client/snd_dma.c b/engine/client/snd_dma.c index b3deaaa2..63b14811 100644 --- a/engine/client/snd_dma.c +++ b/engine/client/snd_dma.c @@ -2081,6 +2081,7 @@ void S_Shutdown(qboolean final) { next = sc->next; sc->Shutdown(sc); + Z_Free(sc->channel); Z_Free(sc); sndcardinfo = next; } diff --git a/engine/client/snd_wasapi.c b/engine/client/snd_wasapi.c index 0b0cc486..b34a92f0 100644 --- a/engine/client/snd_wasapi.c +++ b/engine/client/snd_wasapi.c @@ -203,7 +203,7 @@ static int WASAPI_Thread(void *arg) IMMDevice *pDevice = NULL; IAudioClient *pAudioClient = NULL; IAudioRenderClient *pRenderClient = NULL; - UINT32 bufferFrameCount; + UINT32 bufferFrameCount = 0; HANDLE hEvent = NULL; WAVEFORMATEX *pwfx; diff --git a/engine/client/sys_droid.c b/engine/client/sys_droid.c index 0b6d509f..bb8338ef 100644 --- a/engine/client/sys_droid.c +++ b/engine/client/sys_droid.c @@ -262,6 +262,9 @@ JNIEXPORT void JNICALL Java_com_fteqw_FTEDroidEngine_init(JNIEnv *env, jobject o parms.basedir = sys_basedir; /*filled in later*/ parms.argc = 3; parms.argv = args; +#ifdef CONFIG_MANIFEST_TEXT + parms.manifest = CONFIG_MANIFEST_TEXT; +#endif tmp = (*env)->GetStringUTFChars(env, japkpath, NULL); Q_strncpyz(sys_basepak, tmp, sizeof(sys_basepak)); diff --git a/engine/client/sys_plugfte.c b/engine/client/sys_plugfte.c index ad6bbea6..351bfb4b 100644 --- a/engine/client/sys_plugfte.c +++ b/engine/client/sys_plugfte.c @@ -154,6 +154,27 @@ void VARGS Con_Printf (const char *fmt, ...) Q_vsnprintfz(dest, sizeof(dest), fmt, argptr); va_end (argptr); +#ifdef _WIN32 + OutputDebugString(dest); +#else + FILE *f = fopen("/tmp/ftelog", "a"); + if (f) + { + fputs(dest, f); + fclose(f); + } + write(2, dest, strlen(dest)); +#endif +} +void VARGS Con_DPrintf (const char *fmt, ...) +{ + va_list argptr; + char dest[256]; + + va_start (argptr, fmt); + Q_vsnprintfz(dest, sizeof(dest), fmt, argptr); + va_end (argptr); + #ifdef _WIN32 OutputDebugString(dest); #else diff --git a/engine/client/sys_sdl.c b/engine/client/sys_sdl.c index 16b400f4..7b02d232 100644 --- a/engine/client/sys_sdl.c +++ b/engine/client/sys_sdl.c @@ -462,6 +462,9 @@ int QDECL main(int argc, char **argv) parms.argc = argc; parms.argv = (const char**)argv; +#ifdef CONFIG_MANIFEST_TEXT + parms.manifest = CONFIG_MANIFEST_TEXT; +#endif #ifndef WIN32 fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY); diff --git a/engine/client/sys_xdk.c b/engine/client/sys_xdk.c index 68bc4724..9323af0e 100644 --- a/engine/client/sys_xdk.c +++ b/engine/client/sys_xdk.c @@ -184,7 +184,12 @@ void main( int argc, char **argv) memset(&parms, 0, sizeof(parms)); - //fill in parms + parms.argc = argc; + parms.argv = argv; +#ifdef CONFIG_MANIFEST_TEXT + parms.manifest = CONFIG_MANIFEST_TEXT; +#endif + COM_InitArgv(parms.argc, parms.argv); TL_InitLanguages(parms.basedir); Host_Init(&parms); diff --git a/engine/client/wad.h b/engine/client/wad.h index b91c5b74..d6b3ba94 100644 --- a/engine/client/wad.h +++ b/engine/client/wad.h @@ -44,6 +44,8 @@ typedef struct } qpic_t; typedef struct shader_s shader_t; +#define material_t shader_t //the material +#define rshader_t shader_t //the shader the material will draw with #define mpic_t shader_t //atlased images within some larger atlas diff --git a/engine/client/winquake.rc b/engine/client/winquake.rc index 589b43d0..0af6fc63 100644 --- a/engine/client/winquake.rc +++ b/engine/client/winquake.rc @@ -95,6 +95,10 @@ IDI_ICON1 ICON "bymorphed.ico" IDI_ICON2 ICON "q2.ico" #endif +#ifdef CONFIG_MANIFEST_TEXT +1 RCDATA {CONFIG_MANIFEST_TEXT} +#endif + ///////////////////////////////////////////////////////////////////////////// // // Version diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index 7a526157..f1445560 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -476,6 +476,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define GLESONLY //should reduce the conditions a little #define R_MAX_RECURSE 2 //less bss // #undef RTLIGHTS + #undef HEADLESSQUAKE + #define NO_FREETYPE #endif #ifdef WINRT //microsoft do not support winsock any more. @@ -491,6 +493,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #undef WEBSERVER //http/ftp servers #undef WEBCLIENT //http/ftp clients. #undef MULTITHREAD + #undef HEADLESSQUAKE #endif #ifdef ANDROID #undef RTLIGHTS @@ -499,6 +502,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif #undef TEXTEDITOR #define GLESONLY //should reduce the conditions a little + #undef HEADLESSQUAKE #endif #if defined(NACL) //stuff is sandboxed. @@ -513,6 +517,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #undef IRCCONNECT #define GLSLONLY //pointless having the junk #define GLESONLY //should reduce the conditions a little + #undef HEADLESSQUAKE + #define NO_FREETYPE #endif #ifndef MULTITHREAD @@ -586,7 +592,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #undef TCPCONNECT #undef IRCCONNECT #undef WEBSERVER - #undef WEBCLIENT + #if !defined(FTE_TARGET_WEB) && !defined(NACL) + #undef WEBCLIENT + #endif #endif #ifndef HAVE_PACKET #undef SV_MASTER @@ -730,12 +738,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define ARCH_DL_POSTFIX ".so" #endif -#if defined(_M_AMD64) || defined(__amd64__) -#ifdef _WIN32 - #define ARCH_CPU_POSTFIX "x64" -#else - #define ARCH_CPU_POSTFIX "amd64" -#endif +#if defined(_M_AMD64) || defined(__amd64__) || defined(__x86_64__) + #ifdef __ILP32__ + #define ARCH_CPU_POSTFIX "x32" //32bit pointers, with 16 registers. + #else + #ifdef _WIN32 + #define ARCH_CPU_POSTFIX "x64" + #else + #define ARCH_CPU_POSTFIX "amd64" + #endif + #endif #elif defined(_M_IX86) || defined(__i386__) #define ARCH_CPU_POSTFIX "x86" #elif defined(__powerpc__) || defined(__ppc__) @@ -743,7 +755,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #elif defined(__aarch64__) #define ARCH_CPU_POSTFIX "arm64" #elif defined(__arm__) - #define ARCH_CPU_POSTFIX "arm" + #ifdef __SOFTFP__ + #define ARCH_CPU_POSTFIX "arm" + #else + #define ARCH_CPU_POSTFIX "armhf" + #endif #else #define ARCH_CPU_POSTFIX "unk" #endif diff --git a/engine/common/com_phys_ode.c b/engine/common/com_phys_ode.c index 30ed561a..ad5c17af 100644 --- a/engine/common/com_phys_ode.c +++ b/engine/common/com_phys_ode.c @@ -2737,7 +2737,7 @@ static void QDECL World_ODE_Start(world_t *world) memset(ctx, 0, sizeof(*ctx)); world->rbe = &ctx->pub; - r_meshpitch.value = pCvar_GetFloat("physics_ode_quadtree_depth"); + r_meshpitch.value = pCvar_GetFloat("r_meshpitch"); VectorAvg(world->worldmodel->mins, world->worldmodel->maxs, center); VectorSubtract(world->worldmodel->maxs, center, extents); @@ -2756,7 +2756,7 @@ static void QDECL World_ODE_Start(world_t *world) ctx->pub.RagCreateJoint = World_ODE_RagCreateJoint; ctx->pub.RagDestroyBody = World_ODE_RagDestroyBody; ctx->pub.RagDestroyJoint = World_ODE_RagDestroyJoint; - ctx->pub.Frame = World_ODE_Frame; + ctx->pub.RunFrame = World_ODE_Frame; ctx->pub.PushCommand = World_ODE_PushCommand; if(physics_ode_world_erp->value >= 0) diff --git a/engine/common/common.c b/engine/common/common.c index 9c6dd188..4fd43f5b 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -4067,32 +4067,36 @@ skipwhite: } #define DEFAULT_PUNCTUATION "(,{})(\':;=!><&|+" -char *COM_ParseToken (const char *data, const char *punctuation) +char *COM_ParseTokenOut (const char *data, const char *punctuation, char *token, size_t tokenlen, com_tokentype_t *tokentype) { int c; - int len; + size_t len; if (!punctuation) punctuation = DEFAULT_PUNCTUATION; - COM_AssertMainThread("COM_ParseOut: com_token"); + if (token == com_token || tokentype == &com_tokentype) + COM_AssertMainThread("COM_ParseTokenOut: com_token"); len = 0; - com_token[0] = 0; + token[0] = 0; if (!data) { - com_tokentype = TTP_UNKNOWN; + if (tokentype) + *tokentype = TTP_EOF; return NULL; } // skip whitespace +//line endings count as whitespace only if we can report the token type. skipwhite: - while ( (c = *(unsigned char*)data) <= ' ' && c != '\r' && c != '\n') + while ( (c = *(unsigned char*)data) <= ' ' && ((c != '\r' && c != '\n') || !tokentype)) { if (c == 0) { - com_tokentype = TTP_UNKNOWN; + if (tokentype) + *tokentype = TTP_EOF; return NULL; // end of file; } data++; @@ -4104,24 +4108,25 @@ skipwhite: if (c == '\r' || c == '\n') { - com_tokentype = TTP_LINEENDING; - com_token[0] = '\n'; - com_token[1] = '\0'; + if (tokentype) + *tokentype = TTP_LINEENDING; + token[0] = '\n'; + token[1] = '\0'; data++; return (char*)data; } -// skip // comments +// skip comments if (c=='/') { if (data[1] == '/') - { + { // style comments while (*data && *data != '\n') data++; goto skipwhite; } else if (data[1] == '*') - { + { /* style comments */ data+=2; while (*data && (*data != '*' || data[1] != '/')) data++; @@ -4136,34 +4141,35 @@ skipwhite: // handle quoted strings specially if (c == '\"') { - com_tokentype = TTP_STRING; + if (tokentype) + *tokentype = TTP_STRING; data++; while (1) { if (len >= TOKENSIZE-1) { - com_token[len] = '\0'; + token[len] = '\0'; return (char*)data; } c = *data++; if (c=='\"' || !c) { - com_token[len] = 0; + token[len] = 0; return (char*)data; } - com_token[len] = c; + token[len] = c; len++; } } - com_tokentype = TTP_UNKNOWN; - // parse single characters if (strchr(punctuation, c)) { - com_token[len] = c; + token[len] = c; len++; - com_token[len] = 0; + token[len] = 0; + if (tokentype) + *tokentype = TTP_PUNCTUATION; return (char*)(data+1); } @@ -4172,7 +4178,7 @@ skipwhite: { if (len >= TOKENSIZE-1) break; - com_token[len] = c; + token[len] = c; data++; len++; c = *data; @@ -4180,7 +4186,9 @@ skipwhite: break; } while (c>32); - com_token[len] = 0; + token[len] = 0; + if (tokentype) + *tokentype = TTP_RAWTOKEN; return (char*)data; } @@ -4708,8 +4716,12 @@ void COM_Version_f (void) #ifdef _WIN64 Con_Printf("Compiled for 64bit windows\n"); #endif -#if defined(_M_AMD64) || defined(__amd64__) +#if defined(_M_AMD64) || defined(__amd64__) || defined(__x86_64__) + #ifdef __ILP32__ + Con_Printf("Compiled for AMD64 compatible cpus (x32)\n"); + #else Con_Printf("Compiled for AMD64 compatible cpus\n"); + #endif #endif #ifdef _M_IX86 diff --git a/engine/common/common.h b/engine/common/common.h index 1bfa376f..e109c0ea 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -348,7 +348,7 @@ void deleetstring(char *result, const char *leet); extern char com_token[65536]; -typedef enum {TTP_UNKNOWN, TTP_STRING, TTP_LINEENDING, TTP_RAWTOKEN, TTP_EOF} com_tokentype_t; +typedef enum {TTP_UNKNOWN, TTP_STRING, TTP_LINEENDING, TTP_RAWTOKEN, TTP_EOF, TTP_PUNCTUATION} com_tokentype_t; extern com_tokentype_t com_tokentype; extern qboolean com_eof; @@ -362,7 +362,8 @@ char *COM_ParseStringSet (const char *data, char *out, size_t outlen); //whitesp char *COM_ParseStringSetSep (const char *data, char sep, char *out, size_t outsize); //single-char-separator, no whitespace char *COM_ParseCString (const char *data, char *out, size_t maxoutlen, size_t *writtenlen); char *COM_StringParse (const char *data, char *token, unsigned int tokenlen, qboolean expandmacros, qboolean qctokenize); -char *COM_ParseToken (const char *data, const char *punctuation); +#define COM_ParseToken(data,punct) COM_ParseTokenOut(data, punct, com_token, sizeof(com_token), &com_tokentype) +char *COM_ParseTokenOut (const char *data, const char *punctuation, char *token, size_t tokenlen, com_tokentype_t *tokentype); //note that line endings are a special type of token. char *COM_TrimString(char *str, char *buffer, int buffersize); const char *COM_QuotedString(const char *string, char *buf, int buflen, qboolean omitquotes); //inverse of COM_StringParse diff --git a/engine/common/particles.h b/engine/common/particles.h index b39c28c8..1edc4bb2 100644 --- a/engine/common/particles.h +++ b/engine/common/particles.h @@ -183,8 +183,17 @@ typedef struct trailstate_s { #define PARTICLE_Z_CLIP 8.0 -typedef enum { BM_BLEND/*SRC_ALPHA ONE_MINUS_SRC_ALPHA*/, BM_BLENDCOLOUR/*SRC_COLOR ONE_MINUS_SRC_COLOR*/, BM_ADDA/*SRC_ALPHA ONE*/, BM_ADDC/*GL_SRC_COLOR GL_ONE*/, BM_SUBTRACT/*SRC_ALPHA ONE_MINUS_SRC_COLOR*/, BM_INVMODA/*ZERO ONE_MINUS_SRC_ALPHA*/, BM_INVMODC/*ZERO ONE_MINUS_SRC_COLOR*/, BM_PREMUL/*ONE ONE_MINUS_SRC_ALPHA*/} blendmode_t; - +typedef enum { + BM_BLEND/*SRC_ALPHA ONE_MINUS_SRC_ALPHA*/, + BM_BLENDCOLOUR/*SRC_COLOR ONE_MINUS_SRC_COLOR*/, + BM_ADDA/*SRC_ALPHA ONE*/, + BM_ADDC/*GL_SRC_COLOR GL_ONE*/, + BM_SUBTRACT/*SRC_ALPHA ONE_MINUS_SRC_COLOR*/, + BM_INVMODA/*ZERO ONE_MINUS_SRC_ALPHA*/, + BM_INVMODC/*ZERO ONE_MINUS_SRC_COLOR*/, + BM_PREMUL/*ONE ONE_MINUS_SRC_ALPHA*/, + BM_RTSMOKE /*special shader generation that causes these particles to be lit up by nearby rtlights, instead of just being fullbright junk*/ +} blendmode_t; #define frandom() (rand()*(1.0f/RAND_MAX)) #define crandom() (rand()*(2.0f/RAND_MAX)-1.0f) #define hrandom() (rand()*(1.0f/RAND_MAX)-0.5f) diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 8da497ea..df4c35ad 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -38,7 +38,7 @@ void PF_Common_RegisterCvars(void) a.i = 1; b.i = 1; if (!(a.f && b.f)) - Con_Printf("WARNING: denormalised floats are disabled. Mods may malfunction\n"); + Con_Printf(CON_WARNING "WARNING: denormalised floats are disabled. Mods may malfunction\n"); Cvar_Register (&sv_gameplayfix_blowupfallenzombies, cvargroup_progs); @@ -5822,6 +5822,23 @@ void QCBUILTIN PF_checkcommand (pubprogfuncs_t *prinst, struct globalvars_s *pr_ G_FLOAT(OFS_RETURN) = 0; } +void QCBUILTIN PF_physics_supported(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ +#ifdef USERBE + world_t *world = prinst->parms->user; + if (prinst->callargc) + { + if (G_FLOAT(OFS_PARM0) && !world->rbe) + World_RBE_Start(world); + else if (!G_FLOAT(OFS_PARM0) && world->rbe) + World_RBE_Shutdown(world); + } + if (world->rbe) + G_FLOAT(OFS_RETURN) = 1; + else +#endif + G_FLOAT(OFS_RETURN) = 0; +} #ifdef USERBE void QCBUILTIN PF_physics_enable(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h index 4d5e160d..901fc1b1 100644 --- a/engine/common/pr_common.h +++ b/engine/common/pr_common.h @@ -266,6 +266,7 @@ void QCBUILTIN PF_setattachment(pubprogfuncs_t *prinst, struct globalvars_s *pr_ void skel_dodelete(world_t *world); void skel_reset(world_t *world); #endif +void QCBUILTIN PF_physics_supported(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); 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); void QCBUILTIN PF_physics_addtorque(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); @@ -755,9 +756,9 @@ enum terrainedit_e ter_reset, //vector pos, float radius ter_reloadsect, //vector pos, float radius - ter_ents_wipe, //none - ter_ents_concat, //string - ter_ents_get, //none + ter_ents_wipe_deprecated, //none + ter_ents_concat_deprecated, //string + ter_ents_get, //none, returns the map's entity string. // ter_poly_add, //add a poly, woo // ter_poly_remove, //remove polys diff --git a/engine/common/world.h b/engine/common/world.h index f5348d8d..2c3f772c 100644 --- a/engine/common/world.h +++ b/engine/common/world.h @@ -166,7 +166,7 @@ typedef struct void (QDECL *RagCreateJoint)(struct world_s *world, rbejoint_t *joint, rbejointinfo_t *info, rbebody_t *body1, rbebody_t *body2, vec3_t aaa2[3]); void (QDECL *RagDestroyBody)(struct world_s *world, rbebody_t *bodyptr); void (QDECL *RagDestroyJoint)(struct world_s *world, rbejoint_t *joint); - void (QDECL *Frame)(struct world_s *world, double frametime, double gravity); + void (QDECL *RunFrame)(struct world_s *world, double frametime, double gravity); void (QDECL *PushCommand)(struct world_s *world, rbecommandqueue_t *cmd); } rigidbodyengine_t; #endif diff --git a/engine/common/zone.c b/engine/common/zone.c index 6a73a798..33bfeca9 100644 --- a/engine/common/zone.c +++ b/engine/common/zone.c @@ -399,7 +399,7 @@ void *Z_Realloc(void *data, int newsize) */ #ifdef USE_MSVCRT_DEBUG -void *BZF_MallocNamed(int size, char *file, int line) //BZ_MallocNamed but allowed to fail - like straight malloc. +void *BZF_MallocNamed(int size, const char *file, int line) //BZ_MallocNamed but allowed to fail - like straight malloc. { void *mem; mem = _malloc_dbg(size, _NORMAL_BLOCK, file, line); @@ -425,7 +425,7 @@ void *BZF_Malloc(int size) //BZ_Malloc but allowed to fail - like straight mallo #endif #ifdef USE_MSVCRT_DEBUG -void *BZ_MallocNamed(int size, char *file, int line) //BZ_MallocNamed but allowed to fail - like straight malloc. +void *BZ_MallocNamed(int size, const char *file, int line) //BZ_MallocNamed but allowed to fail - like straight malloc. { void *mem = BZF_MallocNamed(size, file, line); if (!mem) @@ -445,12 +445,12 @@ void *BZ_Malloc(int size) //Doesn't clear. The expectation is a large file, rath #endif #ifdef USE_MSVCRT_DEBUG -void *BZF_ReallocNamed(void *data, int newsize, char *file, int line) +void *BZF_ReallocNamed(void *data, int newsize, const char *file, int line) { return _realloc_dbg(data, newsize, _NORMAL_BLOCK, file, line); } -void *BZ_ReallocNamed(void *data, int newsize, char *file, int line) +void *BZ_ReallocNamed(void *data, int newsize, const char *file, int line) { void *mem = BZF_ReallocNamed(data, newsize, file, line); diff --git a/engine/common/zone.h b/engine/common/zone.h index 78b160d5..45d8527e 100644 --- a/engine/common/zone.h +++ b/engine/common/zone.h @@ -96,19 +96,19 @@ void *VARGS Z_TagMalloc (int size, int tag); void VARGS Z_TagFree(void *ptr); void VARGS Z_FreeTags(int tag); qboolean ZF_ReallocElements(void **ptr, size_t *elements, size_t newelements, size_t elementsize); //returns false on error -qboolean ZF_ReallocElementsNamed(void **ptr, size_t *elements, size_t newelements, size_t elementsize, char *file, int line); //returns false on error +qboolean ZF_ReallocElementsNamed(void **ptr, size_t *elements, size_t newelements, size_t elementsize, const char *file, int line); //returns false on error #define Z_ReallocElements(ptr,elements,newelements,elementsize) do{if (!ZF_ReallocElements(ptr,elements,newelements,elementsize))Sys_Error("Z_ReallocElements failed (%s %i)\n", __FILE__, __LINE__);}while(0) //returns false on error //Big Zone: allowed to fail, doesn't clear. The expectation is a large file, rather than sensitive data structures. //(this is a nicer name for malloc) void *BZ_Malloc(int size); void *BZF_Malloc(int size); -void *BZ_MallocNamed (int size, char *file, int line); // returns 0 filled memory -void *BZF_MallocNamed (int size, char *file, int line); // allowed to fail +void *BZ_MallocNamed (int size, const char *file, int line); // returns 0 filled memory +void *BZF_MallocNamed (int size, const char *file, int line); // allowed to fail void *BZ_Realloc(void *ptr, int size); -void *BZ_ReallocNamed(void *data, int newsize, char *file, int line); +void *BZ_ReallocNamed(void *data, int newsize, const char *file, int line); void *BZF_Realloc(void *data, int newsize); -void *BZF_ReallocNamed(void *data, int newsize, char *file, int line); +void *BZF_ReallocNamed(void *data, int newsize, const char *file, int line); void BZ_Free(void *ptr); //ctx should start off as void*ctx=NULL diff --git a/engine/dotnet2005/ftequake.vcproj b/engine/dotnet2005/ftequake.vcproj index 88fc45f6..d5f18507 100644 --- a/engine/dotnet2005/ftequake.vcproj +++ b/engine/dotnet2005/ftequake.vcproj @@ -24919,6 +24919,202 @@ /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + numentityinfo) Mod_ParseEntities(mod); + //make sure we don't have any cached entities string, by wiping it all. + Z_Free((char*)mod->entities_raw); + mod->entities_raw = NULL; + + G_INT(OFS_RETURN) = 0; if (idx < mod->numentityinfo) { + if (!G_INT(OFS_PARM2) && !mod->entityinfo[idx].keyvals) + return; //no-op Z_Free(mod->entityinfo[idx].keyvals); mod->entityinfo[idx].keyvals = NULL; + id = mod->entityinfo[idx].id; } + else + id = 0; if (G_INT(OFS_PARM2)) { newvals = PR_GetStringOfs(prinst, OFS_PARM2); if (idx >= mod->numentityinfo) Z_ReallocElements((void**)&mod->entityinfo, &mod->numentityinfo, idx+64, sizeof(*mod->entityinfo)); mod->entityinfo[idx].keyvals = Z_StrDup(newvals); + if (!id) + id = (idx+1) | ((cl.playerview[0].playernum+1)<<24); + mod->entityinfo[idx].id = id; } else + { newvals = NULL; - G_INT(OFS_RETURN) = 0; + mod->entityinfo[idx].id = 0; + } #ifndef CLIENTONLY if (sv.state && modelindex > 0) @@ -4877,11 +4894,13 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g MSG_WriteByte(&sv.multicast, svcfte_brushedit); MSG_WriteShort(&sv.multicast, modelindex); MSG_WriteByte(&sv.multicast, newvals?hmcmd_ent_edit:hmcmd_ent_remove); - MSG_WriteLong(&sv.multicast, idx); + MSG_WriteLong(&sv.multicast, id); if (newvals) MSG_WriteString(&sv.multicast, newvals); SV_MulticastProtExt(vec3_origin, MULTICAST_ALL_R, ~0, 0, 0); //tell ssqc, csqc will be told by the server + + SSQC_MapEntityEdited(modelindex, idx, newvals); } else #endif @@ -4891,19 +4910,19 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g MSG_WriteByte(&cls.netchan.message, clcfte_brushedit); MSG_WriteShort(&cls.netchan.message, modelindex); MSG_WriteByte(&cls.netchan.message, newvals?hmcmd_ent_edit:hmcmd_ent_remove); - MSG_WriteLong(&cls.netchan.message, idx); + MSG_WriteLong(&cls.netchan.message, id); if (newvals) MSG_WriteString(&cls.netchan.message, newvals); #ifdef CSQC_DAT - CSQC_MapEntityEdited(idx, newvals); + CSQC_MapEntityEdited(modelindex, idx, newvals); #endif } else #endif { #ifdef CSQC_DAT - CSQC_MapEntityEdited(idx, newvals); + CSQC_MapEntityEdited(modelindex, idx, newvals); #endif } } @@ -4913,7 +4932,6 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g // int idx = G_INT(OFS_PARM1); // const char *news = PR_GetStringOfs(prinst, OFS_PARM2); G_INT(OFS_RETURN) = mod->numentityinfo; - } return; case ter_ent_count: @@ -4921,11 +4939,11 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g Mod_ParseEntities(mod); G_INT(OFS_RETURN) = mod->numentityinfo; return; - case ter_ents_wipe: + case ter_ents_wipe_deprecated: G_INT(OFS_RETURN) = PR_TempString(prinst, Mod_GetEntitiesString(mod)); Mod_SetEntitiesString(mod, "", true); return; - case ter_ents_concat: + case ter_ents_concat_deprecated: { char *newv; const char *olds = Mod_GetEntitiesString(mod); @@ -6197,33 +6215,45 @@ void CL_Parse_BrushEdit(void) else if (cmd == hmcmd_prespawned) { } - else if (cmd == hmcmd_ent_edit) + else if (cmd == hmcmd_ent_edit || cmd == hmcmd_ent_remove) { //ent edit int id = MSG_ReadLong(); - const char *data = MSG_ReadString(); - if (id > 0xffff) - return; - if (id >= mod->numentityinfo) - Z_ReallocElements((void**)&mod->entityinfo, &mod->numentityinfo, id+64, sizeof(*mod->entityinfo)); - if (id < mod->numentityinfo) + const char *data; + int idx = mod->numentityinfo, i; + if (cmd == hmcmd_ent_edit) + data = MSG_ReadString(); + else + data = NULL; + + //convert id to idx + for (i = 0; i < mod->numentityinfo; i++) + { + if (mod->entityinfo[i].id == id) + { + idx = i; + break; + } + if (!mod->entityinfo[i].keyvals) + idx = i; + } + + //FIXME: cap the maximum data sizes (both count and storage, to prevent DOS attacks). + + if (idx == mod->numentityinfo && data) + Z_ReallocElements((void**)&mod->entityinfo, &mod->numentityinfo, mod->numentityinfo+64, sizeof(*mod->entityinfo)); + if (idx < mod->numentityinfo) { if (!ignore) { - Z_Free(mod->entityinfo[id].keyvals); - mod->entityinfo[id].keyvals = Z_StrDup(data); + mod->entityinfo[idx].id = id; + Z_Free(mod->entityinfo[idx].keyvals); + if (data) + mod->entityinfo[idx].keyvals = Z_StrDup(data); + else + mod->entityinfo[idx].keyvals = NULL; + + CSQC_MapEntityEdited(modelindex, idx, data); } - CSQC_MapEntityEdited(id, data); - } - } - else if (cmd == hmcmd_ent_remove) - { - int id = MSG_ReadLong(); - if (sv.state >= ss_loading) - return; //if we're the server then this will already have been done. don't clobber from internal latency - if (id < mod->numentityinfo) - { - Z_Free(mod->entityinfo[id].keyvals); - mod->entityinfo[id].keyvals = NULL; } } else @@ -6350,16 +6380,20 @@ qboolean SV_Parse_BrushEdit(void) } else if (cmd == hmcmd_ent_edit || cmd == hmcmd_ent_remove) { - size_t entid = MSG_ReadLong(); + unsigned int entid = MSG_ReadLong(); char *keyvals = (cmd == hmcmd_ent_edit)?MSG_ReadString():NULL; if (mod->submodelof != mod) return true; if (!authorise) { SV_PrintToClient(host_client, PRINT_MEDIUM, "Entity editing ignored: you are not a mapper\n"); + //FIXME: undo the client's edit? or is that rude? return true; } + //FIXME: need to update the server's entity list + //SSQC_MapEntityEdited(idx, newvals); + MSG_WriteByte(&sv.multicast, svcfte_brushedit); MSG_WriteShort(&sv.multicast, modelindex); MSG_WriteByte(&sv.multicast, keyvals?hmcmd_ent_edit:hmcmd_ent_remove); @@ -6471,7 +6505,7 @@ void QCBUILTIN PF_brush_create(pubprogfuncs_t *prinst, struct globalvars_s *pr_g model_t *mod = vmw->Get_CModel(vmw, modelindex); heightmap_t *hm = mod?mod->terrain:NULL; unsigned int numfaces = G_INT(OFS_PARM2); - qcbrushface_t *in_faces = validateqcpointer(prinst, G_INT(OFS_PARM1), sizeof(*in_faces), numfaces, false); + qcbrushface_t *in_faces = validateqcpointer(prinst, G_INT(OFS_PARM1), sizeof(*in_faces), numfaces, numfaces==0); unsigned int contents = G_INT(OFS_PARM3); unsigned int brushid = (prinst->callargc > 4)?G_INT(OFS_PARM4):0; //to simplify edits @@ -6999,6 +7033,7 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) int brushcontents = FTECONTENTS_SOLID; heightmap_t *subhm = NULL; model_t *submod = NULL; + const char *brushpunct = "(){}[]"; //use an empty string for better compat with vanilla qbsp... #ifdef RUNTIMELIGHTING hm->entsdirty = true; @@ -7013,7 +7048,7 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) while(entities) { start = entities; - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); if (token[0] == '}' && token[1] == 0) { nest--; @@ -7136,24 +7171,24 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) { if (token[0] != '(' || token[1] != 0) break; - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); points[p][0] = atof(token); - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); points[p][1] = atof(token); - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); points[p][2] = atof(token); - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); if (token[0] != ')' || token[1] != 0) { // VectorClear(points[1]); // VectorClear(points[2]); points[1][0] = atof(token); - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); if (p == 0 && !strcmp(token, ")")) p = 4; //we just managed to read an entire plane instead of 3 points. break; } - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); } if (p < 3) { @@ -7184,35 +7219,35 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) brushcontents = FTECONTENTS_SOLID; //FIXME: halflife format has the entire [x y z dist] plane specified. - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); if (*token == '[') { hlstyle = true; - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); texplane[0][0] = atof(token); - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); texplane[0][1] = atof(token); - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); texplane[0][2] = atof(token); - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); texplane[0][3] = atof(token); - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); //] - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); //[ - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); texplane[1][0] = atof(token); - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); texplane[1][1] = atof(token); - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); texplane[1][2] = atof(token); - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); texplane[1][3] = atof(token); - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); //] } else @@ -7220,22 +7255,22 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) VectorClear(texplane[0]); VectorClear(texplane[1]); texplane[0][3] = atof(token); - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); texplane[1][3] = atof(token); } - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); rot = atof(token); - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); scale[0] = atof(token); - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); scale[1] = atof(token); //hexen2 has some extra junk that is useless - some 'light' value, but its never used and should normally be -1. while (*entities == ' ' || *entities == '\t') entities++; if (*entities == '-' || (*entities >= '0' && *entities <= '9')) - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); //okay, that's all the actual parsing, now try to make sense of this plane. if (p == 4) @@ -7298,13 +7333,13 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) { if (!strcmp(token, "classname")) { - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); if (!strcmp(token, "func_detail")) isdetail = true; } else - entities = COM_ParseOut(entities, token, sizeof(token)); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); } while(start < entities) *out++ = *start++; diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index f74ac0ea..46b9719b 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -586,6 +586,7 @@ void Mod_ParseEntities(model_t *mod) m *= 2; mod->entityinfo = BZ_Realloc(mod->entityinfo, sizeof(*mod->entityinfo) * m); } + mod->entityinfo[c].id = c+1; mod->entityinfo[c].keyvals = BZ_Malloc(entend-entstart + 1); memcpy(mod->entityinfo[c].keyvals, entstart, entend-entstart); mod->entityinfo[c].keyvals[entend-entstart] = 0; @@ -1267,7 +1268,37 @@ static void Mod_LoadModelWorker (void *ctx, void *data, size_t a, size_t b) return; } #endif - break; // failed to load unreplaced file and nothing left +#ifdef TERRAIN + if (!Q_strcasecmp(ext, "map")) + { + const char *dummymap = + "{\n" + "classname worldspawn\n" + "wad \"base.wad\"\n" //we ARE a quake engine after all, and default.wad is generally wrong + "message \"Unnamed map\"\n" + "{\n" + "(-128 128 0) ( 128 128 0) ( 128 -128 0) \"WBRICK1_5\" 0 0 0 1 1\n" + "( 128 -128 -16)( 128 128 -16) (-128 128 -16) \"WBRICK1_5\" 0 0 0 1 1\n" + "( 128 128 0) (-128 128 0) (-128 128 -16) \"WBRICK1_5\" 0 0 0 1 1\n" + "(-128 -128 0) ( 128 -128 0) ( 128 -128 -16) \"WBRICK1_5\" 0 0 0 1 1\n" + "(-128 128 0) (-128 -128 0) (-128 -128 -16) \"WBRICK1_5\" 0 0 0 1 1\n" + "( 128 -128 0) ( 128 128 0) ( 128 128 -16) \"WBRICK1_5\" 0 0 0 1 1\n" + "}\n" + "}\n" + "{\n" + "classname info_player_start\n" + "origin \"0 0 24\"\n" + "}\n" + "{\n" + "classname light\n" + "origin \"0 0 64\"\n" + "}\n"; + buf = (unsigned*)Z_StrDup(dummymap); + filesize = strlen(dummymap); + } + else +#endif + break; // failed to load unreplaced file and nothing left } } if (!buf) diff --git a/engine/gl/gl_model.h b/engine/gl/gl_model.h index 53eb8a2d..b9637d3e 100644 --- a/engine/gl/gl_model.h +++ b/engine/gl/gl_model.h @@ -950,7 +950,7 @@ typedef struct model_s q3lightgridinfo_t *lightgrid; mfog_t *fogs; int numfogs; - struct {char *keyvals;} *entityinfo; + struct {unsigned int id; char *keyvals;} *entityinfo; size_t numentityinfo; const char *entities_raw; int entitiescrc; diff --git a/engine/gl/gl_rlight.c b/engine/gl/gl_rlight.c index b94f422a..eed444a0 100644 --- a/engine/gl/gl_rlight.c +++ b/engine/gl/gl_rlight.c @@ -706,6 +706,14 @@ qboolean R_ImportRTLights(const char *entlump) int nest; qboolean okay = false; + //a quick note about tenebrae: + //by default, tenebrae's rtlights come from the server via static entities, which is all fancy and posh and actually fairly nice... if all servers actually did it. + //(the tenebrae gamecode uses spawnflag 2048 for static lights. note the pflags_fulldynamic fte/dp vs tenebrae difference) + //failing that, it will insert lights with some crappy fixed radius around only all 'classname light' entities, without any colours or anything, vanilla only. + //such lights are ONLY created if they're not near some other existing light (like a static entity one). + //this can result in FTE having noticably more and bigger lights than tenebrae. shadowmapping doesn't help performance either. + float lightmaplevel = -1; + COM_Parse(entlump); if (!strcmp(com_token, "Version")) { @@ -921,6 +929,7 @@ qboolean R_ImportRTLights(const char *entlump) else if (entnum == 0 && !strcmp("lightmapbright", key)) { //tenebrae compat. this overrides r_shadow_realtime_world_lightmap + lightmaplevel = atof(value); } } if (!islight) @@ -1262,7 +1271,9 @@ void R_ReloadRTLights_f(void) R_ImportRTLights(Mod_GetEntitiesString(cl.worldmodel)); else if (!strcmp(Cmd_Argv(1), "rtlights")) R_LoadRTLights(); - else if (strcmp(Cmd_Argv(1), "none")) + else if (!strcmp(Cmd_Argv(1), "none")) + ; + else { R_LoadRTLights(); if (rtlights_first == rtlights_max) diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index e11a64f4..017bc610 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -2354,6 +2354,7 @@ static void Shader_DP_Refract(shader_t *shader, shaderpass_t *pass, char **ptr) static void Shader_BEMode(shader_t *shader, shaderpass_t *pass, char **ptr) { + char subname[1024]; int mode; char tokencopy[1024]; char *token; @@ -2417,8 +2418,7 @@ static void Shader_BEMode(shader_t *shader, shaderpass_t *pass, char **ptr) { if ((mode & LSHADER_CUBE) && (mode & LSHADER_SPOT)) continue; - shader->bemoverrides[mode] = R_RegisterCustom(va("%s%s%s%s%s", - tokencopy, + Q_snprintfz(subname, sizeof(subname), "%s%s%s%s%s", tokencopy, (mode & LSHADER_SMAP)?"#PCF":"", (mode & LSHADER_SPOT)?"#SPOT":"", (mode & LSHADER_CUBE)?"#CUBE":"", @@ -2427,13 +2427,13 @@ static void Shader_BEMode(shader_t *shader, shaderpass_t *pass, char **ptr) #else "" #endif - ) - , shader->usageflags, embed?Shader_DefaultScript:NULL, embed); + ); + shader->bemoverrides[mode] = R_RegisterCustom(subname, shader->usageflags|(embed?SUR_FORCEFALLBACK:0), embed?Shader_DefaultScript:NULL, embed); } } else { - shader->bemoverrides[mode] = R_RegisterCustom(tokencopy, shader->usageflags, embed?Shader_DefaultScript:NULL, embed); + shader->bemoverrides[mode] = R_RegisterCustom(tokencopy, shader->usageflags|(embed?SUR_FORCEFALLBACK:0), embed?Shader_DefaultScript:NULL, embed); } if (embed) BZ_Free(embed); @@ -6363,7 +6363,7 @@ static shader_t *R_LoadShader (const char *name, unsigned int usageflags, shader *argsstart = 0; COM_StripExtension (cleanname, shortname, sizeof(shortname)); - if (ruleset_allow_shaders.ival) + if (ruleset_allow_shaders.ival && !(usageflags & SUR_FORCEFALLBACK)) { if (sh_config.shadernamefmt) { diff --git a/engine/gl/gl_vidcommon.c b/engine/gl/gl_vidcommon.c index cda332d4..e19f9b3b 100644 --- a/engine/gl/gl_vidcommon.c +++ b/engine/gl/gl_vidcommon.c @@ -1725,7 +1725,7 @@ static const char *glsl_hdrs[] = "#else\n" "#ifdef USE_ARB_SHADOW\n" //with arb_shadow, we can benefit from hardware acclerated pcf, for smoother shadows - "#define dosamp(x,y) shadow2D(smap, shadowcoord.xyz + (vec3(x,y,0.0)*l_shadowmapscale.xyx))\n" + "#define dosamp(x,y) float(shadow2D(smap, shadowcoord.xyz + (vec3(x,y,0.0)*l_shadowmapscale.xyx)))\n" "#else\n" "#define dosamp(x,y) float(texture2D(smap, shadowcoord.xy + (vec2(x,y)*l_shadowmapscale.xy)).r >= shadowcoord.z)\n" "#endif\n" @@ -3121,15 +3121,31 @@ void DumpGLState(void) rendererinfo_t openglrendererinfo = { + //customise the text printed depending on the actual type of opengl that we're locking ourselves to #ifdef FTE_TARGET_WEB "WebGL", +#elif defined(GLESONLY) + #ifdef GLSLONLY + "OpenGLES2+", + #else + "OpenGLES", + #endif #else "OpenGL", #endif { + //reorder these too, if only so that 'setrenderer' lists gles-only builds as using gles instead of gl +#if defined(GLESONLY) + "gles", + "opengles", "gl", "opengl", - "hardware", +#else + "gl", + "opengl", + "gles", + "opengles", +#endif }, QR_OPENGL, diff --git a/engine/gl/r_bishaders.h b/engine/gl/r_bishaders.h index 43f43fda..d6c29348 100644 --- a/engine/gl/r_bishaders.h +++ b/engine/gl/r_bishaders.h @@ -4229,6 +4229,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "varying vec2 tc;\n" "varying vec4 vc;\n" "uniform vec4 e_colourident;\n" +"uniform vec4 e_lmscale;\n" "void main ()\n" "{\n" "vec4 col = texture2D(s_t0, tc);\n" @@ -4236,7 +4237,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "if (col.a < float(MASK))\n" "discard;\n" "#endif\n" -"gl_FragColor = fog4blend(col * vc * e_colourident);\n" +"gl_FragColor = fog4blend(col * vc * e_colourident * e_lmscale);\n" "}\n" "#endif\n" }, @@ -9806,6 +9807,9 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#if !defined(TESS_CONTROL_SHADER)\n" "varying vec2 tcbase;\n" "varying vec3 lightvector;\n" +"#if defined(VERTEXCOLOURS)\n" +"varying vec4 vc;\n" +"#endif\n" "#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)\n" "varying vec3 eyevector;\n" "#endif\n" @@ -9838,6 +9842,9 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "lightvector.y = dot(lightminusvertex, t.xyz);\n" "lightvector.z = dot(lightminusvertex, n.xyz);\n" "#endif\n" +"#if defined(VERTEXCOLOURS)\n" +"vc = v_colour;\n" +"#endif\n" "#if defined(SPECULAR)||defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)\n" "vec3 eyeminusvertex = e_eyepos - w.xyz;\n" "eyevector.x = dot(eyeminusvertex, s.xyz);\n" @@ -9877,6 +9884,10 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "out vec2 t_tcbase[];\n" "in vec3 lightvector[];\n" "out vec3 t_lightvector[];\n" +"#if defined(VERTEXCOLOURS)\n" +"in vec4 vc[];\n" +"out vec4 t_vc[];\n" +"#endif\n" "#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)\n" "in vec3 eyevector[];\n" "out vec3 t_eyevector[];\n" @@ -9889,6 +9900,9 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "t_normal[id] = normal[id];\n" "t_tcbase[id] = tcbase[id];\n" "t_lightvector[id] = lightvector[id];\n" +"#if defined(VERTEXCOLOURS)\n" +"t_vc[id] = vc[id];\n" +"#endif\n" "#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)\n" "t_eyevector[id] = eyevector[id];\n" "#endif\n" @@ -9915,6 +9929,9 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "in vec3 t_normal[];\n" "in vec2 t_tcbase[];\n" "in vec3 t_lightvector[];\n" +"#if defined(VERTEXCOLOURS)\n" +"in vec4 t_vc[];\n" +"#endif\n" "#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)\n" "in vec3 t_eyevector[];\n" "#endif\n" @@ -9938,6 +9955,9 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND //FIXME: we should be recalcing these here, instead of just lerping them "lightvector = LERP(t_lightvector);\n" +"#if defined(VERTEXCOLOURS)\n" +"vc = LERP(t_vc);\n" +"#endif\n" "#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)\n" "eyevector = LERP(t_eyevector);\n" "#endif\n" @@ -9984,9 +10004,12 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#define tcbase tcoffsetmap\n" "#endif\n" "#if defined(FLAT)\n" -"vec3 bases = vec3(FLAT);\n" +"vec4 bases = vec3(FLAT, 1.0);\n" "#else\n" -"vec3 bases = vec3(texture2D(s_diffuse, tcbase));\n" +"vec4 bases = texture2D(s_diffuse, tcbase);\n" +"#ifdef VERTEXCOLOURS\n" +"bases.rgb *= bases.a;\n" +"#endif\n" "#endif\n" "#ifdef UPPER\n" "vec4 uc = texture2D(s_upper, tcbase);\n" @@ -10008,14 +10031,14 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "vec3 diff;\n" "#ifdef NOBUMP\n" //surface can only support ambient lighting, even for lights that try to avoid it. -"diff = bases * (l_lightcolourscale.x+l_lightcolourscale.y);\n" +"diff = bases.rgb * (l_lightcolourscale.x+l_lightcolourscale.y);\n" "#else\n" "vec3 nl = normalize(lightvector);\n" "#ifdef BUMP\n" -"diff = bases * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(bumps, nl), 0.0));\n" +"diff = bases.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(bumps, nl), 0.0));\n" "#else\n" //we still do bumpmapping even without bumps to ensure colours are always sane. light.exe does it too. -"diff = bases * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0));\n" +"diff = bases.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0));\n" "#endif\n" "#endif\n" @@ -10042,6 +10065,9 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND /*2d projection, not used*/ // diff *= texture2d(s_projectionmap, shadowcoord); "#endif\n" +"#if defined(VERTEXCOLOURS)\n" +"diff *= vc.rgb * vc.a;\n" +"#endif\n" "gl_FragColor.rgb = fog3additive(diff*colorscale*l_lightcolour);\n" diff --git a/engine/gl/shader.h b/engine/gl/shader.h index 66e85609..0733bbb2 100644 --- a/engine/gl/shader.h +++ b/engine/gl/shader.h @@ -536,13 +536,15 @@ enum bemoverride_fog, //post-render volumetric fog bemoverride_max }; +//FIXME: split into separate materials and shaders struct shader_s { char name[MAX_QPATH]; enum { SUF_NONE = 0, SUF_LIGHTMAP = 1<<0, //$lightmap passes are valid. otherwise collapsed to an rgbgen - SUF_2D = 1<<1 //any loaded textures will obey 2d picmips rather than 3d picmips + SUF_2D = 1<<1, //any loaded textures will obey 2d picmips rather than 3d picmips + SUR_FORCEFALLBACK = 1<<2//shader fallback is forced, will not load from disk } usageflags; // int uses; //released when the uses drops to 0 int width; //when used as an image, this is the logical 'width' of the image. FIXME. @@ -554,8 +556,8 @@ struct shader_s struct shader_s *next; int id; - shader_t *bemoverrides[bemoverride_max]; - shader_t *remapto; //render using this shader instead. for q3 nonsense. + rshader_t *bemoverrides[bemoverride_max]; + material_t *remapto; //render using this material instead. for q3 nonsense. float remaptime; byte_vec4_t fog_color; diff --git a/engine/http/httpclient.c b/engine/http/httpclient.c index 4bd8af4a..fd2679de 100644 --- a/engine/http/httpclient.c +++ b/engine/http/httpclient.c @@ -27,6 +27,7 @@ static void DL_Cancel(struct dl_download *dl) } static void DL_OnLoad(void *c, void *data, int datasize) { + //also fires from 404s. struct dl_download *dl = c; //make sure the file is 'open'. @@ -65,13 +66,15 @@ static void DL_OnError(void *c) #endif { struct dl_download *dl = c; + //fires from cross-domain blocks, tls errors, etc. + //anything which doesn't yield an http response (404 is NOT an error as far as js is aware). #if MYJS dl->replycode = ecode; #else dl->replycode = 404; //we don't actually know. should we not do this? #endif - Con_Printf("download: %s: error %i\n", dl->url, dl->replycode); + Con_Printf(CON_WARNING"dl error: %s\n", dl->url); dl->status = DL_FAILED; } static void DL_OnProgress(void *c, int position, int totalsize) @@ -495,6 +498,7 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) char *nl; char *msg; int ammount; + qboolean transfercomplete = false; #ifdef MULTITHREAD //if we're running in a thread, wait for some actual activity instead of busylooping like an moron. @@ -826,8 +830,11 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) con->bufferused+=ammount; - if (con->chunking) //FIXME: NEEDS TESTING!!! + if (con->chunking) { + //9\r\n + //chunkdata\r\n + //(etc) int trim; char *nl; con->buffer[con->bufferused] = '\0'; @@ -838,29 +845,46 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) trim = con->bufferused - con->chunked; if (trim > con->chunksize) trim = con->chunksize; //don't go into the next size field. - con->chunksize -= trim; - con->chunked += trim; - if (!con->chunksize) + if (con->chunksize == trim) { //we need to find the next \n and trim it. - nl = strchr(con->buffer+con->chunked, '\n'); + nl = strchr(con->buffer+con->chunked+trim, '\n'); if (!nl) break; nl++; + con->chunksize = 0; + con->chunked += trim; + + //chop out the \r\n from the stream trim = nl - (con->buffer+con->chunked); memmove(con->buffer + con->chunked, nl, con->buffer+con->bufferused-nl+1); con->bufferused -= trim; } + else + { + con->chunksize -= trim; + con->chunked += trim; + } + if (!(con->bufferused - con->chunked)) break; } else { + size_t nextsize; nl = strchr(con->buffer+con->chunked, '\n'); if (!nl) break; - con->chunksize = strtol(con->buffer+con->chunked, NULL, 16); //it's hex. + nextsize = strtoul(con->buffer+con->chunked, NULL, 16); //it's hex. nl++; + if (!nextsize) //eof. make sure we skip its \n too + { + nl = strchr(nl, '\n'); + if (!nl) + break; + transfercomplete = true; + } + con->chunksize = nextsize; trim = nl - (con->buffer+con->chunked); memmove(con->buffer + con->chunked, nl, con->buffer+con->bufferused-nl+1); con->bufferused -= trim; @@ -914,11 +938,12 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) con->bufferused -= chunk; } if (con->totalreceived == con->contentlength) - ammount = 0; + transfercomplete = true; } - if (!ammount) - { //server closed off the connection. + if (!ammount || transfercomplete) + { //server closed off the connection (or signalled eof/sent enough data). + //if (ammount) then we can save off the connection for reuse. if (con->chunksize) dl->status = DL_FAILED; else @@ -1716,16 +1741,6 @@ static int QDECL VFSPIPE_ReadBytes(vfsfile_t *f, void *buffer, int len) len = p->writepos - p->readpos; memcpy(buffer, p->data+p->readpos, len); p->readpos += len; - - if (p->readpos > 8192) - { - //shift the memory down periodically - //fixme: use cyclic buffer? max size, etc? - memmove(p->data, p->data+p->readpos, p->writepos-p->readpos); - - p->writepos -= p->readpos; - p->readpos = 0; - } Sys_UnlockMutex(p->mutex); return len; } @@ -1733,6 +1748,12 @@ static int QDECL VFSPIPE_WriteBytes(vfsfile_t *f, const void *buffer, int len) { vfspipe_t *p = (vfspipe_t*)f; Sys_LockMutex(p->mutex); + if (p->readpos > 8192) + { //don't grow infinitely if we're reading+writing at the same time + memmove(p->data, p->data+p->readpos, p->writepos-p->readpos); + p->writepos -= p->readpos; + p->readpos = 0; + } if (p->writepos + len > p->maxlen) { p->maxlen = p->writepos + len; diff --git a/engine/http/iwebiface.c b/engine/http/iwebiface.c index bd288739..73f7e2d0 100644 --- a/engine/http/iwebiface.c +++ b/engine/http/iwebiface.c @@ -253,13 +253,11 @@ skipwhite: return (char*)data; } +#undef COM_ParseToken char *COM_ParseToken (const char *data, const char *punctuation) { int c; int len; -#ifndef WEBSVONLY - COM_AssertMainThread("COM_ParseToken"); -#endif len = 0; com_token[0] = 0; diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index ed03f063..eb59719a 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -3,6 +3,14 @@ #include "qcc.h" #include +/* +TODO: +*foo++ = 5; +is currently store foo->tmp; store 5->*tmp; add tmp,1->foo +should be just store 5->*foo; add foo,1->foo +the dereference does the post-inc. needs to be delayed somehow. could build a list of post-inc terms after the current expression, but might be really messy. +*/ + #define WARN_IMPLICITVARIANTCAST 0 //FIXME: #define IAMNOTLAZY @@ -2460,6 +2468,11 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ optres_constantarithmatic++; return QCC_MakeFloatConst(eval_a->_float + eval_b->_int); + case OP_ADD_PIW: + QCC_FreeTemp(var_a); QCC_FreeTemp(var_b); + optres_constantarithmatic++; + return QCC_MakeIntConst(eval_a->_int + eval_b->_int*4); + case OP_SUB_IF: QCC_FreeTemp(var_a); QCC_FreeTemp(var_b); optres_constantarithmatic++; @@ -2772,7 +2785,7 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ return nd; } break; - +#endif case OP_BITOR_F: case OP_OR_F: case OP_SUB_F: @@ -2813,6 +2826,7 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ case OP_OR_I: case OP_SUB_I: case OP_ADD_I: + case OP_ADD_PIW: if (eval_b->_int == 0) { optres_constantarithmatic++; @@ -2830,12 +2844,13 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ } break; case OP_BITAND_I: - if (eval_b->_int == 0xffffffff) + if (eval_b->_int == ~0) { optres_constantarithmatic++; QCC_FreeTemp(var_b); return var_a; } + break; case OP_AND_I: if (eval_b->_int) { @@ -2850,7 +2865,6 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ return var_b; } break; -#endif } } } @@ -10612,11 +10626,10 @@ void QCC_PR_ParseStatement (void) e = QCC_PR_Expression (TOP_PRIORITY, 0); QCC_PR_Expect(":"); e2 = QCC_PR_Expression (TOP_PRIORITY, 0); + e2 = QCC_SupplyConversion(e2, ev_float, true); if (e.cast->type != ev_entity || e2.cast->type != ev_float) QCC_PR_ParseError(ERR_THINKTIMETYPEMISMATCH, "thinktime type mismatch"); - QCC_SupplyConversion(e2, ev_float, true); - if (QCC_OPCodeValid(&pr_opcodes[OP_THINKTIME])) QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_THINKTIME], e, e2, NULL)); else @@ -13678,7 +13691,7 @@ Called at the outer layer and when a local statement is hit void QCC_PR_ParseDefs (char *classname) { char *name; - QCC_type_t *type, *defclass; + QCC_type_t *basetype, *type, *defclass; QCC_def_t *def, *d; QCC_sref_t dynlength; QCC_function_t *f; @@ -13805,68 +13818,12 @@ void QCC_PR_ParseDefs (char *classname) QCC_PR_GetDef(type_void, "end_sys_globals", NULL, true, 0, false); accglobalsblock = 3; } - } - if (!pr_scope) - switch(accglobalsblock)//reacc support. - { - case 1: + if (!pr_scope) + switch(accglobalsblock)//reacc support. { - char *oldp = pr_file_p; - name = QCC_PR_ParseName(); - if (!QCC_PR_CheckToken(":")) //nope, it wasn't! - { - QCC_PR_IncludeChunk(name, true, NULL); - QCC_PR_Lex(); - QCC_PR_UnInclude(); - pr_file_p = oldp; - break; - } - if (QCC_PR_CheckKeyword(keyword_object, "object")) - QCC_PR_GetDef(type_entity, name, NULL, true, 0, true); - else if (QCC_PR_CheckKeyword(keyword_string, "string")) - QCC_PR_GetDef(type_string, name, NULL, true, 0, true); - else if (QCC_PR_CheckKeyword(keyword_real, "real")) - { - def = QCC_PR_GetDef(type_float, name, NULL, true, 0, true); - if (QCC_PR_CheckToken("=")) + case 1: { - def->symboldata[0]._float = pr_immediate._float; - QCC_PR_Lex(); - } - } - else if (QCC_PR_CheckKeyword(keyword_vector, "vector")) - { - def = QCC_PR_GetDef(type_vector, name, NULL, true, 0, true); - if (QCC_PR_CheckToken("=")) - { - QCC_PR_Expect("["); - def->symboldata[0].vector[0] = pr_immediate._float; - QCC_PR_Lex(); - def->symboldata[0].vector[1] = pr_immediate._float; - QCC_PR_Lex(); - def->symboldata[0].vector[2] = pr_immediate._float; - QCC_PR_Lex(); - QCC_PR_Expect("]"); - } - } - else if (QCC_PR_CheckKeyword(keyword_pfunc, "pfunc")) - QCC_PR_GetDef(type_function, name, NULL, true, 0, true); - else - QCC_PR_ParseError(ERR_BADNOTTYPE, "Bad type\n"); - QCC_PR_Expect (";"); - - if (QCC_PR_CheckKeyword (keyword_system, "system")) - QCC_PR_Expect (";"); - return; - } - case 2: - name = QCC_PR_ParseName(); - QCC_PR_GetDef(type_function, name, NULL, true, 0, true); - QCC_PR_CheckToken (";"); - return; - case 3: - { char *oldp = pr_file_p; name = QCC_PR_ParseName(); if (!QCC_PR_CheckToken(":")) //nope, it wasn't! @@ -13878,41 +13835,97 @@ void QCC_PR_ParseDefs (char *classname) break; } if (QCC_PR_CheckKeyword(keyword_object, "object")) - def = QCC_PR_GetDef(QCC_PR_FieldType(type_entity), name, NULL, true, 0, GDF_CONST|GDF_SAVED); + QCC_PR_GetDef(type_entity, name, NULL, true, 0, true); else if (QCC_PR_CheckKeyword(keyword_string, "string")) - def = QCC_PR_GetDef(QCC_PR_FieldType(type_string), name, NULL, true, 0, GDF_CONST|GDF_SAVED); + QCC_PR_GetDef(type_string, name, NULL, true, 0, true); else if (QCC_PR_CheckKeyword(keyword_real, "real")) - def = QCC_PR_GetDef(QCC_PR_FieldType(type_float), name, NULL, true, 0, GDF_CONST|GDF_SAVED); - else if (QCC_PR_CheckKeyword(keyword_vector, "vector")) - def = QCC_PR_GetDef(QCC_PR_FieldType(type_vector), name, NULL, true, 0, GDF_CONST|GDF_SAVED); - else if (QCC_PR_CheckKeyword(keyword_pfunc, "pfunc")) - def = QCC_PR_GetDef(QCC_PR_FieldType(type_function), name, NULL, true, 0, GDF_CONST|GDF_SAVED); - else { + def = QCC_PR_GetDef(type_float, name, NULL, true, 0, true); + if (QCC_PR_CheckToken("=")) + { + def->symboldata[0]._float = pr_immediate._float; + QCC_PR_Lex(); + } + } + else if (QCC_PR_CheckKeyword(keyword_vector, "vector")) + { + def = QCC_PR_GetDef(type_vector, name, NULL, true, 0, true); + if (QCC_PR_CheckToken("=")) + { + QCC_PR_Expect("["); + def->symboldata[0].vector[0] = pr_immediate._float; + QCC_PR_Lex(); + def->symboldata[0].vector[1] = pr_immediate._float; + QCC_PR_Lex(); + def->symboldata[0].vector[2] = pr_immediate._float; + QCC_PR_Lex(); + QCC_PR_Expect("]"); + } + } + else if (QCC_PR_CheckKeyword(keyword_pfunc, "pfunc")) + QCC_PR_GetDef(type_function, name, NULL, true, 0, true); + else QCC_PR_ParseError(ERR_BADNOTTYPE, "Bad type\n"); + QCC_PR_Expect (";"); + + if (QCC_PR_CheckKeyword (keyword_system, "system")) + QCC_PR_Expect (";"); + return; + } + case 2: + name = QCC_PR_ParseName(); + QCC_PR_GetDef(type_function, name, NULL, true, 0, true); + QCC_PR_CheckToken (";"); + return; + case 3: + { + char *oldp = pr_file_p; + name = QCC_PR_ParseName(); + if (!QCC_PR_CheckToken(":")) //nope, it wasn't! + { + QCC_PR_IncludeChunk(name, true, NULL); + QCC_PR_Lex(); + QCC_PR_UnInclude(); + pr_file_p = oldp; + break; + } + if (QCC_PR_CheckKeyword(keyword_object, "object")) + def = QCC_PR_GetDef(QCC_PR_FieldType(type_entity), name, NULL, true, 0, GDF_CONST|GDF_SAVED); + else if (QCC_PR_CheckKeyword(keyword_string, "string")) + def = QCC_PR_GetDef(QCC_PR_FieldType(type_string), name, NULL, true, 0, GDF_CONST|GDF_SAVED); + else if (QCC_PR_CheckKeyword(keyword_real, "real")) + def = QCC_PR_GetDef(QCC_PR_FieldType(type_float), name, NULL, true, 0, GDF_CONST|GDF_SAVED); + else if (QCC_PR_CheckKeyword(keyword_vector, "vector")) + def = QCC_PR_GetDef(QCC_PR_FieldType(type_vector), name, NULL, true, 0, GDF_CONST|GDF_SAVED); + else if (QCC_PR_CheckKeyword(keyword_pfunc, "pfunc")) + def = QCC_PR_GetDef(QCC_PR_FieldType(type_function), name, NULL, true, 0, GDF_CONST|GDF_SAVED); + else + { + QCC_PR_ParseError(ERR_BADNOTTYPE, "Bad type\n"); + QCC_PR_Expect (";"); + return; + } + + if (!def->initialized) + { + unsigned int u; + def->initialized = 1; + for (u = 0; u < def->type->size*(def->arraysize?def->arraysize:1); u++) //make arrays of fields work. + { + if (*(int *)&def->symboldata[u]) + { + QCC_PR_ParseWarning(0, "Field def already has a value:"); + QCC_PR_ParsePrintDef(0, def); + } + *(int *)&def->symboldata[u] = pr.size_fields+u; + } + + pr.size_fields += u; + } + QCC_PR_Expect (";"); return; } - - if (!def->initialized) - { - unsigned int u; - def->initialized = 1; - for (u = 0; u < def->type->size*(def->arraysize?def->arraysize:1); u++) //make arrays of fields work. - { - if (*(int *)&def->symboldata[u]) - { - QCC_PR_ParseWarning(0, "Field def already has a value:"); - QCC_PR_ParsePrintDef(0, def); - } - *(int *)&def->symboldata[u] = pr.size_fields+u; - } - - pr.size_fields += u; - } - - QCC_PR_Expect (";"); - return; } } @@ -13956,13 +13969,13 @@ void QCC_PR_ParseDefs (char *classname) break; } - type = QCC_PR_ParseType (false, false); - if (type == NULL) //ignore + basetype = QCC_PR_ParseType (false, false); + if (basetype == NULL) //ignore return; inlinefunction = type_inlinefunction; - if (externfnc && type->type != ev_function) + if (externfnc && basetype->type != ev_function) { printf ("Only functions may be defined as external (yet)\n"); externfnc=false; @@ -13972,10 +13985,10 @@ void QCC_PR_ParseDefs (char *classname) { name = QCC_PR_ParseName (); QCC_PR_Expect("("); - type = QCC_PR_ParseFunctionTypeReacc(false, type); + type = QCC_PR_ParseFunctionTypeReacc(false, basetype); QCC_PR_Expect(";"); - def = QCC_PR_GetDef (type, name, NULL, true, 0, false); + def = QCC_PR_GetDef (basetype, name, NULL, true, 0, false); if (autoprototype || dostrip) { //ignore the code and stuff @@ -14018,7 +14031,7 @@ void QCC_PR_ParseDefs (char *classname) { def->referenced = true; - f = QCC_PR_ParseImmediateStatements (def, type, false); + f = QCC_PR_ParseImmediateStatements (def, basetype, false); def->initialized = 1; def->isstatic = isstatic; @@ -14039,6 +14052,7 @@ void QCC_PR_ParseDefs (char *classname) do { isinitialised = false; + type = basetype; if (QCC_PR_CheckToken (";")) { @@ -14062,6 +14076,8 @@ void QCC_PR_ParseDefs (char *classname) } else { + while (QCC_PR_CheckToken ("*")) + type = QCC_PointerTypeTo(type); name = QCC_PR_ParseName (); } diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index a406a69f..6e81ee11 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -4267,9 +4267,9 @@ void QCC_SetDefaultProperties (void) if (qcc_targetformat == QCF_HEXEN2 || qcc_targetformat == QCF_FTEH2) //force on the thinktime keyword if hexen2 progs. { - keyword_thinktime = true; - keyword_until = true; - keyword_loop = true; + keyword_thinktime = true; //thinktime self : 0.1; + keyword_until = true; //until(cond) {code}; or do{code}until(cond); + keyword_loop = true; //loop {code}; } if ((FWDSLASHARGS && QCC_CheckParm("/Debug"))) //disable any debug optimisations diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index b324826d..efa52ebc 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -1031,7 +1031,7 @@ void PR_LoadGlabalStruct(qboolean muted) #endif } -progsnum_t AddProgs(const char *name) +static progsnum_t AddProgs(const char *name) { float fl; func_t f; @@ -1133,7 +1133,7 @@ progsnum_t AddProgs(const char *name) return num; } -void PR_Decompile_f(void) +static void PR_Decompile_f(void) { if (!svprogfuncs) { @@ -1147,7 +1147,7 @@ void PR_Decompile_f(void) else svprogfuncs->Decompile(svprogfuncs, Cmd_Argv(1)); } -void PR_Compile_f(void) +static void PR_Compile_f(void) { qboolean killondone = false; int argc=3; @@ -1202,7 +1202,7 @@ void PR_Compile_f(void) Con_TPrintf("Compile took %f secs\n", time); } -void PR_ApplyCompilation_f (void) +static void PR_ApplyCompilation_f (void) { edict_t *ent; char *s; @@ -1241,7 +1241,7 @@ void PR_ApplyCompilation_f (void) svprogfuncs->parms->memfree(s); } -void PR_BreakPoint_f(void) +static void PR_BreakPoint_f(void) { int wasset; int isset; @@ -1265,7 +1265,7 @@ void PR_BreakPoint_f(void) // Cvar_Set(Cvar_FindVar("debugger"), "1"); } -void PR_WatchPoint_f(void) +static void PR_WatchPoint_f(void) { char *variable = Cmd_Argv(1); int oldself; @@ -1320,7 +1320,7 @@ static void PR_SSPoke_f(void) Con_TPrintf ("not supported.\n"); } -void PR_SSCoreDump_f(void) +static void PR_SSCoreDump_f(void) { if (!svprogfuncs) { @@ -1337,7 +1337,7 @@ void PR_SSCoreDump_f(void) } } -void PR_SVExtensionList_f(void); +static void PR_SVExtensionList_f(void); /* #ifdef _DEBUG @@ -2127,6 +2127,10 @@ qboolean PR_ParseClusterEvent(char *dest, char *source, char *cmd, char *info) return false; } +void SSQC_MapEntityEdited(int modelidx, int idx, const char *newdata) +{ +} + qboolean PR_KrimzonParseCommand(char *s) { globalvars_t *pr_globals; @@ -10662,6 +10666,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs // {"matchpattern", PF_Fixme, 0, 0, 0, 538, "float(string s, string pattern, float matchrule)"}, // {"undefined", PF_Fixme, 0, 0, 0, 539, ""}, + {"physics_supported",PF_physics_supported,0, 0, 0, 0, D("float(optional float force)", "Queries whether rigid body physics is enabled or not. CSQC and SSQC may report different values. If the force argument is used then the engine will try to activate or release physics (returning the new state, which may fail if plugins or dlls are missing). Note that restarting the physics engine is likely to result in hitches when collision trees get generated. The state may change if a plugin is disabled mid-map.")}, #ifdef USERBE {"physics_enable", PF_physics_enable, 0, 0, 0, 540, D("void(entity e, float physics_enabled)", "Enable or disable the physics attached to a MOVETYPE_PHYSICS entity. Entities which have been disabled in this way will stop taking so much cpu time.")}, {"physics_addforce",PF_physics_addforce,0, 0, 0, 541, D("void(entity e, vector force, vector relative_ofs)", "Apply some impulse directional force upon a MOVETYPE_PHYSICS entity.")}, @@ -11985,13 +11990,13 @@ void PR_DumpPlatform_f(void) {"TEREDIT_TINT", "const float", CS, NULL, ter_tint}, {"TEREDIT_RESET_SECT", "const float", CS, NULL, ter_reset}, {"TEREDIT_RELOAD_SECT", "const float", CS, NULL, ter_reloadsect}, - {"TEREDIT_ENTS_WIPE", "const float", CS, NULL, ter_ents_wipe}, +// {"TEREDIT_ENTS_WIPE", "const float", CS, NULL, ter_ents_wipe_deprecated}, // {"TEREDIT_ENTS_CONCAT", "const float", CS, NULL, ter_ents_concat}, // {"TEREDIT_ENTS_GET", "const float", CS, NULL, ter_ents_get}, {"TEREDIT_ENT_GET", "const float", CS, NULL, ter_ent_get}, {"TEREDIT_ENT_SET", "const float", CS, NULL, ter_ent_set}, {"TEREDIT_ENT_ADD", "const float", CS, NULL, ter_ent_add}, - {"TEREDIT_ENT_COUNT", "const float", CS, NULL, ter_ent_count}, + {"TEREDIT_ENT_COUNT", "const float", CS, NULL, ter_ent_count}, #endif {"SLIST_HOSTCACHEVIEWCOUNT", "const float", CS|MENU, NULL, SLIST_HOSTCACHEVIEWCOUNT}, diff --git a/engine/server/progs.h b/engine/server/progs.h index 9b04c771..c68bda95 100644 --- a/engine/server/progs.h +++ b/engine/server/progs.h @@ -35,6 +35,7 @@ void PR_SpawnInitialEntities(const char *file); void PR_RegisterFields(void); void PR_Init(void); void QDECL ED_Spawned (struct edict_s *ent, int loading); +void SSQC_MapEntityEdited(int modelidx, int idx, const char *newdata); qboolean SV_RunFullQCMovement(struct client_s *client, usercmd_t *ucmd); qboolean PR_KrimzonParseCommand(char *s); qboolean PR_ParseClusterEvent(char *dest, char *source, char *cmd, char *info); diff --git a/engine/server/server.h b/engine/server/server.h index 5d4ffe5b..34a85383 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -1060,7 +1060,9 @@ void SV_AutoAddPenalty (client_t *cl, unsigned int banflag, int duration, char * #define BAN_USER6 (1u<<16)//mod-specified #define BAN_USER7 (1u<<17)//mod-specified #define BAN_USER8 (1u<<18)//mod-specified -#define BAN_MAPPER (1u<<19)//mod-specified +#define BAN_MAPPER (1u<<19)//player is allowed to use the brush/entity editing clc. + +#define BAN_NOLOCALHOST (BAN_BAN|BAN_PERMIT|BAN_SPECONLY) //any attempt to ban localhost is denied, but you can vip, lag, cripple, etc them. // // sv_main.c diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index 524b771b..950f2c95 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -556,6 +556,7 @@ void SV_Map_f (void) COM_FlushFSCache(false, true); +#ifdef Q2SERVER if (strlen(level) > 4 && (!strcmp(level + strlen(level)-4, ".cin") || !strcmp(level + strlen(level)-4, ".roq") || @@ -565,6 +566,13 @@ void SV_Map_f (void) cinematic = true; } else +#endif +#ifdef TERRAIN + //'map doesntexist.map' should just auto-generate that map or something + if (!Q_strcasecmp("map", COM_FileExtension(level, expanded, sizeof(expanded)))) + ; + else +#endif { char *exts[] = {"maps/%s", "maps/%s.bsp", "maps/%s.cm", "maps/%s.hmp", /*"maps/%s.map",*/ NULL}; int i, j; @@ -1037,6 +1045,11 @@ void SV_EvaluatePenalties(client_t *cl) } } } + + if (delta & BAN_VIP) + Info_SetValueForStarKey(cl->userinfo, "*VIP", (cl->penalties & BAN_VIP)?"1":"", sizeof(cl->userinfo)); + if (delta & BAN_MAPPER) + Info_SetValueForStarKey(cl->userinfo, "*mapper", (cl->penalties & BAN_MAPPER)?"1":"", sizeof(cl->userinfo)); } static time_t reevaluatebantime; @@ -1225,12 +1238,6 @@ static void SV_FilterIP_f (void) return; } - if (NET_IsLoopBackAddress(&proto.adr)) - { - Con_Printf("You're not allowed to filter loopback!\n"); - return; - } - s = Cmd_Argv(2); proto.banflags = 0; while(*s) @@ -1258,6 +1265,12 @@ static void SV_FilterIP_f (void) proto.banflags = filterban.ival?BAN_BAN:BAN_PERMIT; } + if (NET_IsLoopBackAddress(&proto.adr) && (proto.banflags & BAN_NOLOCALHOST)) + { //do allow them to be muted etc, just not banned outright. + Con_Printf("You're not allowed to filter loopback!\n"); + return; + } + s = Cmd_Argv(3); if (*s == '+') { @@ -1461,6 +1474,12 @@ static void SV_PenaltyToggle (unsigned int banflag, char *penaltyname) proto.adr.port = 0; proto.adrmask.type = cl->netchan.remote_address.type; + if (NET_IsLoopBackAddress(&proto.adr) && (proto.banflags & BAN_NOLOCALHOST)) + { + Con_Printf("You're not allowed to filter loopback!\n"); + continue; + } + switch(SV_ToggleBan(&proto, reason)) { case 1: diff --git a/engine/server/sv_mvd.c b/engine/server/sv_mvd.c index 5acd08be..86639f72 100644 --- a/engine/server/sv_mvd.c +++ b/engine/server/sv_mvd.c @@ -781,7 +781,7 @@ static int QDECL Sys_listdirFound(const char *fname, qofs_t fsize, time_t mtime, return true; } -static int Sys_listdir_Sort(const void *va, const void *vb) +static int QDECL Sys_listdir_Sort(const void *va, const void *vb) { const file_t *fa = va; const file_t *fb = vb; diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index cebb42e4..c47d55ce 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -2553,7 +2553,7 @@ qboolean SV_Physics (void) #ifdef RAGDOLL rag_doallanimations(&sv.world); #endif - sv.world.rbe->Frame(&sv.world, host_frametime, sv_gravity.value); + sv.world.rbe->RunFrame(&sv.world, host_frametime, sv_gravity.value); } #endif diff --git a/engine/server/sv_sys_unix.c b/engine/server/sv_sys_unix.c index 8603590e..28d296d0 100644 --- a/engine/server/sv_sys_unix.c +++ b/engine/server/sv_sys_unix.c @@ -717,6 +717,9 @@ int main(int argc, char *argv[]) COM_InitArgv (argc, (const char **)argv); parms.argc = com_argc; parms.argv = com_argv; +#ifdef CONFIG_MANIFEST_TEXT + parms.manifest = CONFIG_MANIFEST_TEXT; +#endif #ifdef __linux__ if (!COM_CheckParm("-nodumpstack")) diff --git a/engine/server/world.c b/engine/server/world.c index ba854a68..b96d9075 100644 --- a/engine/server/world.c +++ b/engine/server/world.c @@ -2158,6 +2158,7 @@ void World_RBE_Shutdown(world_t *world) } } world->rbe->End(world); + world->rbe = NULL; #endif } void QDECL World_UnregisterPhysicsEngine(const char *enginename) diff --git a/engine/shaders/glsl/defaultsprite.glsl b/engine/shaders/glsl/defaultsprite.glsl index 85007fe3..e3d19571 100644 --- a/engine/shaders/glsl/defaultsprite.glsl +++ b/engine/shaders/glsl/defaultsprite.glsl @@ -20,6 +20,7 @@ uniform sampler2D s_t0; varying vec2 tc; varying vec4 vc; uniform vec4 e_colourident; +uniform vec4 e_lmscale; void main () { vec4 col = texture2D(s_t0, tc); @@ -27,6 +28,6 @@ void main () if (col.a < float(MASK)) discard; #endif - gl_FragColor = fog4blend(col * vc * e_colourident); + gl_FragColor = fog4blend(col * vc * e_colourident * e_lmscale); } #endif diff --git a/engine/shaders/glsl/rtlight.glsl b/engine/shaders/glsl/rtlight.glsl index 40678015..915f5b97 100644 --- a/engine/shaders/glsl/rtlight.glsl +++ b/engine/shaders/glsl/rtlight.glsl @@ -43,6 +43,9 @@ #if !defined(TESS_CONTROL_SHADER) varying vec2 tcbase; varying vec3 lightvector; + #if defined(VERTEXCOLOURS) + varying vec4 vc; + #endif #if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) varying vec3 eyevector; #endif @@ -75,6 +78,9 @@ void main () lightvector.y = dot(lightminusvertex, t.xyz); lightvector.z = dot(lightminusvertex, n.xyz); #endif +#if defined(VERTEXCOLOURS) + vc = v_colour; +#endif #if defined(SPECULAR)||defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) vec3 eyeminusvertex = e_eyepos - w.xyz; eyevector.x = dot(eyeminusvertex, s.xyz); @@ -114,6 +120,10 @@ in vec2 tcbase[]; out vec2 t_tcbase[]; in vec3 lightvector[]; out vec3 t_lightvector[]; +#if defined(VERTEXCOLOURS) +in vec4 vc[]; +out vec4 t_vc[]; +#endif #if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) in vec3 eyevector[]; out vec3 t_eyevector[]; @@ -126,6 +136,9 @@ void main() t_normal[id] = normal[id]; t_tcbase[id] = tcbase[id]; t_lightvector[id] = lightvector[id]; +#if defined(VERTEXCOLOURS) + t_vc[id] = vc[id]; +#endif #if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) t_eyevector[id] = eyevector[id]; #endif @@ -152,6 +165,9 @@ in vec3 t_vertex[]; in vec3 t_normal[]; in vec2 t_tcbase[]; in vec3 t_lightvector[]; +#if defined(VERTEXCOLOURS) +in vec4 t_vc[]; +#endif #if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) in vec3 t_eyevector[]; #endif @@ -175,6 +191,9 @@ void main() //FIXME: we should be recalcing these here, instead of just lerping them lightvector = LERP(t_lightvector); +#if defined(VERTEXCOLOURS) + vc = LERP(t_vc); +#endif #if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) eyevector = LERP(t_eyevector); #endif @@ -221,9 +240,12 @@ void main () #define tcbase tcoffsetmap #endif #if defined(FLAT) - vec3 bases = vec3(FLAT); + vec4 bases = vec3(FLAT, 1.0); #else - vec3 bases = vec3(texture2D(s_diffuse, tcbase)); + vec4 bases = texture2D(s_diffuse, tcbase); + #ifdef VERTEXCOLOURS + bases.rgb *= bases.a; + #endif #endif #ifdef UPPER vec4 uc = texture2D(s_upper, tcbase); @@ -245,14 +267,14 @@ void main () vec3 diff; #ifdef NOBUMP //surface can only support ambient lighting, even for lights that try to avoid it. - diff = bases * (l_lightcolourscale.x+l_lightcolourscale.y); + diff = bases.rgb * (l_lightcolourscale.x+l_lightcolourscale.y); #else vec3 nl = normalize(lightvector); #ifdef BUMP - diff = bases * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(bumps, nl), 0.0)); + diff = bases.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(bumps, nl), 0.0)); #else //we still do bumpmapping even without bumps to ensure colours are always sane. light.exe does it too. - diff = bases * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0)); + diff = bases.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0)); #endif #endif @@ -278,6 +300,9 @@ void main () #if defined(PROJECTION) /*2d projection, not used*/ // diff *= texture2d(s_projectionmap, shadowcoord); +#endif +#if defined(VERTEXCOLOURS) + diff *= vc.rgb * vc.a; #endif gl_FragColor.rgb = fog3additive(diff*colorscale*l_lightcolour); diff --git a/engine/web/ftejslib.js b/engine/web/ftejslib.js index b1bede06..a1bd4ce8 100644 --- a/engine/web/ftejslib.js +++ b/engine/web/ftejslib.js @@ -825,9 +825,9 @@ console.log("onload: " + _url + " status " + http.status); http.onerror = function(e) { -console.log("onerror: " + _url + " status " + http.status); +console.log("onerror: " + _url); if (onerror) - Runtime.dynCall('vii', onerror, [ctx, http.status]); + Runtime.dynCall('vii', onerror, [ctx, 0]); }; http.onprogress = function(e) diff --git a/engine/web/sys_web.c b/engine/web/sys_web.c index fae7d6be..f3e4e1c1 100644 --- a/engine/web/sys_web.c +++ b/engine/web/sys_web.c @@ -214,6 +214,9 @@ int QDECL main(int argc, char **argv) parms.argc = argc; parms.argv = (const char**)argv; +#ifdef CONFIG_MANIFEST_TEXT + parms.manifest = CONFIG_MANIFEST_TEXT; +#endif COM_InitArgv (parms.argc, parms.argv); diff --git a/plugins/Makefile b/plugins/Makefile index 3128ec62..23cbdb5d 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -1,18 +1,18 @@ #windows is special as always, but we don't support itanium, and microsoft don't support anything else (not even arm with the nt win32 api) ifeq ($(FTE_TARGET),win32) -PLUG_NATIVE_EXT=_x86.dll + PLUG_NATIVE_EXT=_x86.dll PLUG_LDFLAGS= PLUG_LDFLAGS_ZLIB=-L../engine/libs/mingw-libs -lzlib -BITS=32 -PLUG_LDFLAGS_DL= + BITS=32 + PLUG_LDFLAGS_DL= endif ifeq ($(FTE_TARGET),win64) -PLUG_NATIVE_EXT=_x64.dll + PLUG_NATIVE_EXT=_x64.dll PLUG_LDFLAGS=-Wl,--support-old-code PLUG_LDFLAGS_ZLIB=-L../engine/libs/mingw64-libs -lz -BITS=64 -PLUG_LDFLAGS_DL= + BITS=64 + PLUG_LDFLAGS_DL= endif PLUG_LDFLAGS_DL?=-ldl @@ -20,50 +20,50 @@ PLUG_LDFLAGS?=-L/usr/local/lib -Wl,-R/usr/local/lib -lm PLUG_LDFLAGS_ZLIB?=-lz ifneq ($(PLUG_NATIVE_EXT),) -#if we're on windows, we'll put our windows-specific hacks here. -PLUG_DEFFILE=plugin.def -PLUG_CFLAGS= -PLUG_CXXFLAGS= + #if we're on windows, we'll put our windows-specific hacks here. + PLUG_DEFFILE=plugin.def + PLUG_CFLAGS= + PLUG_CXXFLAGS= endif #cygwin uses dll naming. ifeq ($(FTE_TARGET),cygwin) -ifeq ($(BITS),64) -PLUG_DEFFILE=plugin.def -PLUG_NATIVE_EXT=_amd64.dll -endif -ifneq ($(BITS),64) -PLUG_DEFFILE=plugin.def -PLUG_NATIVE_EXT=_x86.dll -endif + ifeq ($(BITS),64) + PLUG_DEFFILE=plugin.def + PLUG_NATIVE_EXT=_amd64.dll + endif + ifneq ($(BITS),64) + PLUG_DEFFILE=plugin.def + PLUG_NATIVE_EXT=_x86.dll + endif endif #if they're not on windows, we'll try asking the compiler directly #the check to see if its already set is to avoid asking msvc, which would probably break things. ifeq ($(PLUG_NATIVE_EXT),) LIBRESOLV=-lresolv -ifneq ($(shell echo|$(CC) -E -dM -|grep __amd64__),) - #either x32 or x64 ABIs - ifneq ($(shell echo|$(CC) -E -dM -|grep __ILP32__),) - PLUG_NATIVE_EXT=_x32.so - else - PLUG_NATIVE_EXT=_amd64.so + ifneq ($(shell echo|$(CC) -E -dM -|grep __amd64__),) + #either x32 or x64 ABIs + ifneq ($(shell echo|$(CC) -E -dM -|grep __ILP32__|grep 1),) + PLUG_NATIVE_EXT=_x32.so + else + PLUG_NATIVE_EXT=_amd64.so + endif endif -endif -ifneq ($(shell echo|$(CC) -E -dM -|grep __i386__),) - PLUG_NATIVE_EXT=_x86.so -endif -ifneq ($(shell echo|$(CC) -E -dM -|grep __arm__),) - #gnueabi[hf] - ifneq ($(shell echo|$(CC) -E -dM -|grep __SOFTFP__),) - PLUG_NATIVE_EXT=_arm.so - else - PLUG_NATIVE_EXT=_armhf.so + ifneq ($(shell echo|$(CC) -E -dM -|grep __i386__),) + PLUG_NATIVE_EXT=_x86.so + endif + ifneq ($(shell echo|$(CC) -E -dM -|grep __arm__),) + #gnueabi[hf] + ifneq ($(shell echo|$(CC) -E -dM -|grep __SOFTFP__),) + PLUG_NATIVE_EXT=_arm.so + else + PLUG_NATIVE_EXT=_armhf.so + endif + endif + ifneq ($(shell echo|$(CC) -E -dM -|grep __ppc__),) + PLUG_NATIVE_EXT=_ppc.so endif -endif -ifneq ($(shell echo|$(CC) -E -dM -|grep __ppc__),) - PLUG_NATIVE_EXT=_ppc.so -endif endif #fallback diff --git a/plugins/bullet/bulletplug.cpp b/plugins/bullet/bulletplug.cpp new file mode 100644 index 00000000..653037fa --- /dev/null +++ b/plugins/bullet/bulletplug.cpp @@ -0,0 +1,1766 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +//if we're not building as an fte-specific plugin, we must be being built as part of the fte engine itself. +//(no, we don't want to act as a plugin for ezquake...) +#ifndef FTEPLUGIN +#define FTEENGINE +#define FTEPLUGIN +#define pCvar_Register Cvar_Get +#define pCvar_GetFloat(x) Cvar_FindVar(x)->value +#define pSys_Error Sys_Error +#define Plug_Init Plug_Bullet_Init +#pragma comment(lib,"../../plugins/bullet/libs/bullet_dbg.lib") +#endif + +#include "../plugin.h" +#include "../engine.h" + +#include "pr_common.h" +#include "com_mesh.h" + +#ifndef FTEENGINE +#define BZ_Malloc malloc +#define BZ_Free free +#define Z_Free BZ_Free +static vec3_t vec3_origin; +static int VectorCompare (const vec3_t v1, const vec3_t v2) +{ + int i; + for (i=0 ; i<3 ; i++) + if (v1[i] != v2[i]) + return 0; + return 1; +} + +#endif +static rbeplugfuncs_t *rbefuncs; + + +//============================================================================ +// physics engine support +//============================================================================ + +#define DEG2RAD(d) (d * M_PI * (1/180.0f)) +#define RAD2DEG(d) ((d*180) / M_PI) + +#include + + +static void World_Bullet_RunCmd(world_t *world, rbecommandqueue_t *cmd); + +void World_Bullet_Init(void) +{ + pCvar_Register("physics_bullet_enable", "1", 0, "Bullet"); + pCvar_Register("physics_bullet_maxiterationsperframe", "10", 0, "Bullet"); + pCvar_Register("physics_bullet_framerate", "60", 0, "Bullet"); +} + +void World_Bullet_Shutdown(void) +{ +} + +typedef struct bulletcontext_s +{ + rigidbodyengine_t funcs; + + qboolean hasextraobjs; +// void *ode_space; +// void *ode_contactgroup; + // number of constraint solver iterations to use (for dWorldStepFast) +// int ode_iterations; + // actual step (server frametime / ode_iterations) +// vec_t ode_step; + // max velocity for a 1-unit radius object at current step to prevent + // missed collisions +// vec_t ode_movelimit; + rbecommandqueue_t *cmdqueuehead; + rbecommandqueue_t *cmdqueuetail; + + + world_t *gworld; + + + btBroadphaseInterface *broadphase; + btDefaultCollisionConfiguration *collisionconfig; + btCollisionDispatcher *collisiondispatcher; + btSequentialImpulseConstraintSolver *solver; + btDiscreteDynamicsWorld *dworld; + btOverlapFilterCallback *ownerfilter; +} bulletcontext_t; + +class QCFilterCallback : public btOverlapFilterCallback +{ + // return true when pairs need collision + virtual bool needBroadphaseCollision(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) const + { + //dimensions don't collide + bool collides = (proxy0->m_collisionFilterGroup & proxy1->m_collisionFilterMask) != 0; + collides = collides && (proxy1->m_collisionFilterGroup & proxy0->m_collisionFilterMask); + + //owners don't collide (unless one is world, obviouslyish) + if (collides) + { + btRigidBody *b1 = (btRigidBody*)proxy0->m_clientObject; + btRigidBody *b2 = (btRigidBody*)proxy1->m_clientObject; + //don't let two qc-controlled entities collide in Bullet, that's the job of quake. + if (b1->isStaticOrKinematicObject() && b2->isStaticOrKinematicObject()) + return false; + wedict_t *e1 = (wedict_t*)b1->getUserPointer(); + wedict_t *e2 = (wedict_t*)b2->getUserPointer(); + if ((e1->v->solid == SOLID_TRIGGER && e2->v->solid != SOLID_BSP) || + (e2->v->solid == SOLID_TRIGGER && e1->v->solid != SOLID_BSP)) + return false; //triggers only collide with bsp objects. + if (e1->entnum && e2->entnum) + collides = e1->v->owner != e2->entnum && e2->v->owner != e1->entnum; + } + + return collides; + } +}; + +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->ownerfilter; + Z_Free(ctx); +} + +static void QDECL World_Bullet_RemoveJointFromEntity(world_t *world, wedict_t *ed) +{ + ed->ode.ode_joint_type = 0; +// if(ed->ode.ode_joint) +// dJointDestroy((dJointID)ed->ode.ode_joint); + ed->ode.ode_joint = NULL; +} + +static void QDECL World_Bullet_RemoveFromEntity(world_t *world, wedict_t *ed) +{ + struct bulletcontext_s *ctx = (struct bulletcontext_s*)world->rbe; + btRigidBody *body; + btCollisionShape *geom; + if (!ed->ode.ode_physics) + return; + + // entity is not physics controlled, free any physics data + ed->ode.ode_physics = qfalse; + + body = (btRigidBody*)ed->ode.ode_body; + ed->ode.ode_body = NULL; + if (body) + ctx->dworld->removeRigidBody (body); + + geom = (btCollisionShape*)ed->ode.ode_geom; + ed->ode.ode_geom = NULL; + if (ed->ode.ode_geom) + delete geom; + + //FIXME: joints + rbefuncs->ReleaseCollisionMesh(ed); + if(ed->ode.ode_massbuf) + BZ_Free(ed->ode.ode_massbuf); + ed->ode.ode_massbuf = NULL; +} + +static void World_Bullet_Frame_BodyToEntity(world_t *world, wedict_t *ed) +{ + return; + +#if 0 + model_t *model; + const float *avel; + const float *o; + const float *r; // for some reason dBodyGetRotation returns a [3][4] matrix + const float *vel; + btRigidBody *body = (btRigidBody*)ed->ode.ode_body; + int movetype; + float bodymatrix[16]; + float entitymatrix[16]; + vec3_t angles; + vec3_t avelocity; + vec3_t forward, left, up; + vec3_t origin; + vec3_t spinvelocity; + vec3_t velocity; + if (!body) + return; + + movetype = (int)ed->v->movetype; + if (movetype != MOVETYPE_PHYSICS) + { + switch((int)ed->xv->jointtype) + { + // TODO feed back data from physics + case JOINTTYPE_POINT: + break; + case JOINTTYPE_HINGE: + break; + case JOINTTYPE_SLIDER: + break; + case JOINTTYPE_UNIVERSAL: + break; + case JOINTTYPE_HINGE2: + break; + case JOINTTYPE_FIXED: + break; + } + return; + } + // store the physics engine data into the entity + + btTransform trans; + body->getMotionState()->getWorldTransform(trans); +// o = dBodyGetPosition(body); +// r = dBodyGetRotation(body); +// vel = dBodyGetLinearVel(body); +// avel = dBodyGetAngularVel(body); +// VectorCopy(o, origin); +// forward[0] = r[0]; +// forward[1] = r[4]; +// forward[2] = r[8]; +// left[0] = r[1]; +// left[1] = r[5]; +// left[2] = r[9]; +// up[0] = r[2]; +// up[1] = r[6]; +// up[2] = r[10]; + vel = body->getLinearVelocity(); + avel = body->getAngularVelocity(); + VectorCopy(vel, velocity); + VectorCopy(avel, spinvelocity); + trans.getBasis().getOpenGLSubMatrix(bodymatrix); + foo Matrix4x4_RM_FromVectors(bodymatrix, forward, left, up, origin); + foo Matrix4_Multiply(ed->ode.ode_offsetimatrix, bodymatrix, entitymatrix); + foo Matrix3x4_RM_ToVectors(entitymatrix, forward, left, up, origin); + + VectorAngles(forward, up, angles); + angles[0]*=-1; + + avelocity[PITCH] = RAD2DEG(spinvelocity[PITCH]); + avelocity[YAW] = RAD2DEG(spinvelocity[ROLL]); + avelocity[ROLL] = RAD2DEG(spinvelocity[YAW]); + + if (ed->v->modelindex) + { + model = world->Get_CModel(world, ed->v->modelindex); + if (!model || model->type == mod_alias) + { + angles[PITCH] *= -1; + avelocity[PITCH] *= -1; + } + } + + VectorCopy(origin, ed->v->origin); + VectorCopy(velocity, ed->v->velocity); + //VectorCopy(forward, ed->xv->axis_forward); + //VectorCopy(left, ed->xv->axis_left); + //VectorCopy(up, ed->xv->axis_up); + //VectorCopy(spinvelocity, ed->xv->spinvelocity); + VectorCopy(angles, ed->v->angles); + VectorCopy(avelocity, ed->v->avelocity); + + // values for BodyFromEntity to check if the qc modified anything later + VectorCopy(origin, ed->ode.ode_origin); + VectorCopy(velocity, ed->ode.ode_velocity); + VectorCopy(angles, ed->ode.ode_angles); + VectorCopy(avelocity, ed->ode.ode_avelocity); +// ed->ode.ode_gravity = (qboolean)dBodyGetGravityMode(body); + + World_LinkEdict(world, ed, true); +#endif +} + +static void World_Bullet_Frame_JointFromEntity(world_t *world, wedict_t *ed) +{ +#if 0 + dJointID j = 0; + dBodyID b1 = 0; + dBodyID b2 = 0; + int movetype = 0; + int jointtype = 0; + int enemy = 0, aiment = 0; + wedict_t *o; + vec3_t origin, velocity, angles, forward, left, up, movedir; + vec_t CFM, ERP, FMax, Stop, Vel; + VectorClear(origin); + VectorClear(velocity); + VectorClear(angles); + VectorClear(movedir); + movetype = (int)ed->v->movetype; + jointtype = (int)ed->xv->jointtype; + enemy = ed->v->enemy; + aiment = ed->v->aiment; + VectorCopy(ed->v->origin, origin); + VectorCopy(ed->v->velocity, velocity); + VectorCopy(ed->v->angles, angles); + VectorCopy(ed->v->movedir, movedir); + if(movetype == MOVETYPE_PHYSICS) + jointtype = 0; // can't have both + + o = (wedict_t*)PROG_TO_EDICT(world->progs, enemy); + if(o->isfree || o->ode.ode_body == 0) + enemy = 0; + o = (wedict_t*)PROG_TO_EDICT(world->progs, aiment); + if(o->isfree || o->ode.ode_body == 0) + aiment = 0; + // see http://www.ode.org/old_list_archives/2006-January/017614.html + // we want to set ERP? make it fps independent and work like a spring constant + // note: if movedir[2] is 0, it becomes ERP = 1, CFM = 1.0 / (H * K) + if(movedir[0] > 0 && movedir[1] > 0) + { + float K = movedir[0]; + float D = movedir[1]; + float R = 2.0 * D * sqrt(K); // we assume D is premultiplied by sqrt(sprungMass) + CFM = 1.0 / (world->ode.ode_step * K + R); // always > 0 + ERP = world->ode.ode_step * K * CFM; + Vel = 0; + FMax = 0; + Stop = movedir[2]; + } + else if(movedir[1] < 0) + { + CFM = 0; + ERP = 0; + Vel = movedir[0]; + FMax = -movedir[1]; // TODO do we need to multiply with world.physics.ode_step? + Stop = movedir[2] > 0 ? movedir[2] : dInfinity; + } + else // movedir[0] > 0, movedir[1] == 0 or movedir[0] < 0, movedir[1] >= 0 + { + CFM = 0; + ERP = 0; + Vel = 0; + FMax = 0; + Stop = dInfinity; + } + if(jointtype == ed->ode.ode_joint_type && VectorCompare(origin, ed->ode.ode_joint_origin) && VectorCompare(velocity, ed->ode.ode_joint_velocity) && VectorCompare(angles, ed->ode.ode_joint_angles) && enemy == ed->ode.ode_joint_enemy && aiment == ed->ode.ode_joint_aiment && VectorCompare(movedir, ed->ode.ode_joint_movedir)) + return; // nothing to do + AngleVectorsFLU(angles, forward, left, up); + switch(jointtype) + { + case JOINTTYPE_POINT: + j = dJointCreateBall(world->ode.ode_world, 0); + break; + case JOINTTYPE_HINGE: + j = dJointCreateHinge(world->ode.ode_world, 0); + break; + case JOINTTYPE_SLIDER: + j = dJointCreateSlider(world->ode.ode_world, 0); + break; + case JOINTTYPE_UNIVERSAL: + j = dJointCreateUniversal(world->ode.ode_world, 0); + break; + case JOINTTYPE_HINGE2: + j = dJointCreateHinge2(world->ode.ode_world, 0); + break; + case JOINTTYPE_FIXED: + j = dJointCreateFixed(world->ode.ode_world, 0); + break; + case 0: + default: + // no joint + j = 0; + break; + } + if(ed->ode.ode_joint) + { + //Con_Printf("deleted old joint %i\n", (int) (ed - prog->edicts)); + dJointAttach(ed->ode.ode_joint, 0, 0); + dJointDestroy(ed->ode.ode_joint); + } + ed->ode.ode_joint = (void *) j; + ed->ode.ode_joint_type = jointtype; + ed->ode.ode_joint_enemy = enemy; + ed->ode.ode_joint_aiment = aiment; + VectorCopy(origin, ed->ode.ode_joint_origin); + VectorCopy(velocity, ed->ode.ode_joint_velocity); + VectorCopy(angles, ed->ode.ode_joint_angles); + VectorCopy(movedir, ed->ode.ode_joint_movedir); + if(j) + { + //Con_Printf("made new joint %i\n", (int) (ed - prog->edicts)); + dJointSetData(j, (void *) ed); + if(enemy) + b1 = (dBodyID)((WEDICT_NUM(world->progs, enemy))->ode.ode_body); + if(aiment) + b2 = (dBodyID)((WEDICT_NUM(world->progs, aiment))->ode.ode_body); + dJointAttach(j, b1, b2); + + switch(jointtype) + { + case JOINTTYPE_POINT: + dJointSetBallAnchor(j, origin[0], origin[1], origin[2]); + break; + case JOINTTYPE_HINGE: + dJointSetHingeAnchor(j, origin[0], origin[1], origin[2]); + dJointSetHingeAxis(j, forward[0], forward[1], forward[2]); + dJointSetHingeParam(j, dParamFMax, FMax); + dJointSetHingeParam(j, dParamHiStop, Stop); + dJointSetHingeParam(j, dParamLoStop, -Stop); + dJointSetHingeParam(j, dParamStopCFM, CFM); + dJointSetHingeParam(j, dParamStopERP, ERP); + dJointSetHingeParam(j, dParamVel, Vel); + break; + case JOINTTYPE_SLIDER: + dJointSetSliderAxis(j, forward[0], forward[1], forward[2]); + dJointSetSliderParam(j, dParamFMax, FMax); + dJointSetSliderParam(j, dParamHiStop, Stop); + dJointSetSliderParam(j, dParamLoStop, -Stop); + dJointSetSliderParam(j, dParamStopCFM, CFM); + dJointSetSliderParam(j, dParamStopERP, ERP); + dJointSetSliderParam(j, dParamVel, Vel); + break; + case JOINTTYPE_UNIVERSAL: + dJointSetUniversalAnchor(j, origin[0], origin[1], origin[2]); + dJointSetUniversalAxis1(j, forward[0], forward[1], forward[2]); + dJointSetUniversalAxis2(j, up[0], up[1], up[2]); + dJointSetUniversalParam(j, dParamFMax, FMax); + dJointSetUniversalParam(j, dParamHiStop, Stop); + dJointSetUniversalParam(j, dParamLoStop, -Stop); + dJointSetUniversalParam(j, dParamStopCFM, CFM); + dJointSetUniversalParam(j, dParamStopERP, ERP); + dJointSetUniversalParam(j, dParamVel, Vel); + dJointSetUniversalParam(j, dParamFMax2, FMax); + dJointSetUniversalParam(j, dParamHiStop2, Stop); + dJointSetUniversalParam(j, dParamLoStop2, -Stop); + dJointSetUniversalParam(j, dParamStopCFM2, CFM); + dJointSetUniversalParam(j, dParamStopERP2, ERP); + dJointSetUniversalParam(j, dParamVel2, Vel); + break; + case JOINTTYPE_HINGE2: + dJointSetHinge2Anchor(j, origin[0], origin[1], origin[2]); + dJointSetHinge2Axis1(j, forward[0], forward[1], forward[2]); + dJointSetHinge2Axis2(j, velocity[0], velocity[1], velocity[2]); + dJointSetHinge2Param(j, dParamFMax, FMax); + dJointSetHinge2Param(j, dParamHiStop, Stop); + dJointSetHinge2Param(j, dParamLoStop, -Stop); + dJointSetHinge2Param(j, dParamStopCFM, CFM); + dJointSetHinge2Param(j, dParamStopERP, ERP); + dJointSetHinge2Param(j, dParamVel, Vel); + dJointSetHinge2Param(j, dParamFMax2, FMax); + dJointSetHinge2Param(j, dParamHiStop2, Stop); + dJointSetHinge2Param(j, dParamLoStop2, -Stop); + dJointSetHinge2Param(j, dParamStopCFM2, CFM); + dJointSetHinge2Param(j, dParamStopERP2, ERP); + dJointSetHinge2Param(j, dParamVel2, Vel); + break; + case JOINTTYPE_FIXED: + break; + case 0: + default: + break; + } +#undef SETPARAMS + + } +#endif +} + +static qboolean QDECL World_Bullet_RagMatrixToBody(rbebody_t *bodyptr, float *mat) +{ + btRigidBody *body; + +/* + dVector3 r[3]; + + r[0][0] = mat[0]; + r[0][1] = mat[1]; + r[0][2] = mat[2]; + r[1][0] = mat[4]; + r[1][1] = mat[5]; + r[1][2] = mat[6]; + r[2][0] = mat[8]; + r[2][1] = mat[9]; + r[2][2] = mat[10]; + + dBodySetPosition(bodyptr->ode_body, mat[3], mat[7], mat[11]); + dBodySetRotation(bodyptr->ode_body, r[0]); + dBodySetLinearVel(bodyptr->ode_body, 0, 0, 0); + dBodySetAngularVel(bodyptr->ode_body, 0, 0, 0); +*/ + return qtrue; +} +static qboolean QDECL World_Bullet_RagCreateBody(world_t *world, rbebody_t *bodyptr, rbebodyinfo_t *bodyinfo, float *mat, wedict_t *ent) +{ +/* + dMass mass; + float radius; + if (!world->ode.ode_space) + return false; + world->ode.hasodeents = true; //I don't like this, but we need the world etc to be solid. + world->ode.hasextraobjs = true; + + switch(bodyinfo->geomshape) + { + case GEOMTYPE_CAPSULE: + radius = (bodyinfo->dimensions[0] + bodyinfo->dimensions[1]) * 0.5; + bodyptr->ode_geom = (void *)dCreateCapsule(world->ode.ode_space, radius, bodyinfo->dimensions[2]); + dMassSetCapsuleTotal(&mass, bodyinfo->mass, 3, radius, bodyinfo->dimensions[2]); + //aligned along the geom's local z axis + break; + case GEOMTYPE_SPHERE: + //radius + radius = (bodyinfo->dimensions[0] + bodyinfo->dimensions[1] + bodyinfo->dimensions[2]) / 3; + bodyptr->ode_geom = dCreateSphere(world->ode.ode_space, radius); + dMassSetSphereTotal(&mass, bodyinfo->mass, radius); + //aligned along the geom's local z axis + break; + case GEOMTYPE_CYLINDER: + //radius, length + radius = (bodyinfo->dimensions[0] + bodyinfo->dimensions[1]) * 0.5; + bodyptr->ode_geom = dCreateCylinder(world->ode.ode_space, radius, bodyinfo->dimensions[2]); + dMassSetCylinderTotal(&mass, bodyinfo->mass, 3, radius, bodyinfo->dimensions[2]); + //alignment is irreleevnt, thouse I suppose it might be scaled wierdly. + break; + default: + case GEOMTYPE_BOX: + //diameter + bodyptr->ode_geom = dCreateBox(world->ode.ode_space, bodyinfo->dimensions[0], bodyinfo->dimensions[1], bodyinfo->dimensions[2]); + dMassSetBoxTotal(&mass, bodyinfo->mass, bodyinfo->dimensions[0], bodyinfo->dimensions[1], bodyinfo->dimensions[2]); + //monkey + break; + } + bodyptr->ode_body = dBodyCreate(world->ode.ode_world); + dBodySetMass(bodyptr->ode_body, &mass); + dGeomSetBody(bodyptr->ode_geom, bodyptr->ode_body); + dGeomSetData(bodyptr->ode_geom, (void*)ent); +*/ + return World_Bullet_RagMatrixToBody(bodyptr, mat); +} + +static void QDECL World_Bullet_RagMatrixFromJoint(rbejoint_t *joint, rbejointinfo_t *info, float *mat) +{ +/* + dVector3 dr3; + switch(info->type) + { + case JOINTTYPE_POINT: + dJointGetBallAnchor(joint->ode_joint, dr3); + mat[3] = dr3[0]; + mat[7] = dr3[1]; + mat[11] = dr3[2]; + VectorClear(mat+4); + VectorClear(mat+8); + break; + + case JOINTTYPE_HINGE: + dJointGetHingeAnchor(joint->ode_joint, dr3); + mat[3] = dr3[0]; + mat[7] = dr3[1]; + mat[11] = dr3[2]; + + dJointGetHingeAxis(joint->ode_joint, dr3); + VectorCopy(dr3, mat+4); + VectorClear(mat+8); + + CrossProduct(mat+4, mat+8, mat+0); + return; + break; + case JOINTTYPE_HINGE2: + dJointGetHinge2Anchor(joint->ode_joint, dr3); + mat[3] = dr3[0]; + mat[7] = dr3[1]; + mat[11] = dr3[2]; + + dJointGetHinge2Axis1(joint->ode_joint, dr3); + VectorCopy(dr3, mat+4); + dJointGetHinge2Axis2(joint->ode_joint, dr3); + VectorCopy(dr3, mat+8); + break; + + case JOINTTYPE_SLIDER: + //no anchor point... + //get the two bodies and average their origin for a somewhat usable representation of an anchor. + { + const dReal *p1, *p2; + dReal n[3]; + dBodyID b1 = dJointGetBody(joint->ode_joint, 0), b2 = dJointGetBody(joint->ode_joint, 1); + if (b1) + p1 = dBodyGetPosition(b1); + else + { + p1 = n; + VectorClear(n); + } + if (b2) + p2 = dBodyGetPosition(b2); + else + p2 = p1; + dJointGetSliderAxis(joint->ode_joint, dr3 + 0); + VectorInterpolate(p1, 0.5, p2, dr3); + mat[3] = dr3[0]; + mat[7] = dr3[1]; + mat[11] = dr3[2]; + + VectorClear(mat+4); + VectorClear(mat+8); + } + break; + + case JOINTTYPE_UNIVERSAL: + dJointGetUniversalAnchor(joint->ode_joint, dr3); + mat[3] = dr3[0]; + mat[7] = dr3[1]; + mat[11] = dr3[2]; + + dJointGetUniversalAxis1(joint->ode_joint, dr3); + VectorCopy(dr3, mat+4); + dJointGetUniversalAxis2(joint->ode_joint, dr3); + VectorCopy(dr3, mat+8); + + CrossProduct(mat+4, mat+8, mat+0); + return; + break; + } + AngleVectorsFLU(vec3_origin, mat+0, mat+4, mat+8); +*/ +} + +static void QDECL World_Bullet_RagMatrixFromBody(world_t *world, rbebody_t *bodyptr, float *mat) +{ +/* + const dReal *o = dBodyGetPosition(bodyptr->ode_body); + const dReal *r = dBodyGetRotation(bodyptr->ode_body); + mat[0] = r[0]; + mat[1] = r[1]; + mat[2] = r[2]; + mat[3] = o[0]; + + mat[4] = r[4]; + mat[5] = r[5]; + mat[6] = r[6]; + mat[7] = o[1]; + + mat[8] = r[8]; + mat[9] = r[9]; + mat[10] = r[10]; + mat[11] = o[2]; +*/ +} +static void QDECL World_Bullet_RagEnableJoint(rbejoint_t *joint, qboolean enabled) +{ +/* + if (enabled) + dJointEnable(joint->ode_joint); + else + dJointDisable(joint->ode_joint); +*/ +} +static void QDECL World_Bullet_RagCreateJoint(world_t *world, rbejoint_t *joint, rbejointinfo_t *info, rbebody_t *body1, rbebody_t *body2, vec3_t aaa2[3]) +{ +/* + switch(info->type) + { + case JOINTTYPE_POINT: + joint->ode_joint = dJointCreateBall(world->ode.ode_world, 0); + break; + case JOINTTYPE_HINGE: + joint->ode_joint = dJointCreateHinge(world->ode.ode_world, 0); + break; + case JOINTTYPE_SLIDER: + joint->ode_joint = dJointCreateSlider(world->ode.ode_world, 0); + break; + case JOINTTYPE_UNIVERSAL: + joint->ode_joint = dJointCreateUniversal(world->ode.ode_world, 0); + break; + case JOINTTYPE_HINGE2: + joint->ode_joint = dJointCreateHinge2(world->ode.ode_world, 0); + break; + case JOINTTYPE_FIXED: + joint->ode_joint = dJointCreateFixed(world->ode.ode_world, 0); + break; + default: + joint->ode_joint = NULL; + break; + } + if (joint->ode_joint) + { + //Con_Printf("made new joint %i\n", (int) (ed - prog->edicts)); +// dJointSetData(joint->ode_joint, NULL); + dJointAttach(joint->ode_joint, body1?body1->ode_body:NULL, body2?body2->ode_body:NULL); + + switch(info->type) + { + case JOINTTYPE_POINT: + dJointSetBallAnchor(joint->ode_joint, aaa2[0][0], aaa2[0][1], aaa2[0][2]); + break; + case JOINTTYPE_HINGE: + dJointSetHingeAnchor(joint->ode_joint, aaa2[0][0], aaa2[0][1], aaa2[0][2]); + dJointSetHingeAxis(joint->ode_joint, aaa2[1][0], aaa2[1][1], aaa2[1][2]); + dJointSetHingeParam(joint->ode_joint, dParamFMax, info->FMax); + dJointSetHingeParam(joint->ode_joint, dParamHiStop, info->HiStop); + dJointSetHingeParam(joint->ode_joint, dParamLoStop, info->LoStop); + dJointSetHingeParam(joint->ode_joint, dParamStopCFM, info->CFM); + dJointSetHingeParam(joint->ode_joint, dParamStopERP, info->ERP); + dJointSetHingeParam(joint->ode_joint, dParamVel, info->Vel); + break; + case JOINTTYPE_SLIDER: + dJointSetSliderAxis(joint->ode_joint, aaa2[1][0], aaa2[1][1], aaa2[1][2]); + dJointSetSliderParam(joint->ode_joint, dParamFMax, info->FMax); + dJointSetSliderParam(joint->ode_joint, dParamHiStop, info->HiStop); + dJointSetSliderParam(joint->ode_joint, dParamLoStop, info->LoStop); + dJointSetSliderParam(joint->ode_joint, dParamStopCFM, info->CFM); + dJointSetSliderParam(joint->ode_joint, dParamStopERP, info->ERP); + dJointSetSliderParam(joint->ode_joint, dParamVel, info->Vel); + break; + case JOINTTYPE_UNIVERSAL: + dJointSetUniversalAnchor(joint->ode_joint, aaa2[0][0], aaa2[0][1], aaa2[0][2]); + dJointSetUniversalAxis1(joint->ode_joint, aaa2[1][0], aaa2[1][1], aaa2[1][2]); + dJointSetUniversalAxis2(joint->ode_joint, aaa2[2][0], aaa2[2][1], aaa2[2][2]); + dJointSetUniversalParam(joint->ode_joint, dParamFMax, info->FMax); + dJointSetUniversalParam(joint->ode_joint, dParamHiStop, info->HiStop); + dJointSetUniversalParam(joint->ode_joint, dParamLoStop, info->LoStop); + dJointSetUniversalParam(joint->ode_joint, dParamStopCFM, info->CFM); + dJointSetUniversalParam(joint->ode_joint, dParamStopERP, info->ERP); + dJointSetUniversalParam(joint->ode_joint, dParamVel, info->Vel); + dJointSetUniversalParam(joint->ode_joint, dParamFMax2, info->FMax2); + dJointSetUniversalParam(joint->ode_joint, dParamHiStop2, info->HiStop2); + dJointSetUniversalParam(joint->ode_joint, dParamLoStop2, info->LoStop2); + dJointSetUniversalParam(joint->ode_joint, dParamStopCFM2, info->CFM2); + dJointSetUniversalParam(joint->ode_joint, dParamStopERP2, info->ERP2); + dJointSetUniversalParam(joint->ode_joint, dParamVel2, info->Vel2); + break; + case JOINTTYPE_HINGE2: + dJointSetHinge2Anchor(joint->ode_joint, aaa2[0][0], aaa2[0][1], aaa2[0][2]); + dJointSetHinge2Axis1(joint->ode_joint, aaa2[1][0], aaa2[1][1], aaa2[1][2]); + dJointSetHinge2Axis2(joint->ode_joint, aaa2[2][0], aaa2[2][1], aaa2[2][2]); + dJointSetHinge2Param(joint->ode_joint, dParamFMax, info->FMax); + dJointSetHinge2Param(joint->ode_joint, dParamHiStop, info->HiStop); + dJointSetHinge2Param(joint->ode_joint, dParamLoStop, info->LoStop); + dJointSetHinge2Param(joint->ode_joint, dParamStopCFM, info->CFM); + dJointSetHinge2Param(joint->ode_joint, dParamStopERP, info->ERP); + dJointSetHinge2Param(joint->ode_joint, dParamVel, info->Vel); + dJointSetHinge2Param(joint->ode_joint, dParamFMax2, info->FMax2); + dJointSetHinge2Param(joint->ode_joint, dParamHiStop2, info->HiStop2); + dJointSetHinge2Param(joint->ode_joint, dParamLoStop2, info->LoStop2); + dJointSetHinge2Param(joint->ode_joint, dParamStopCFM2, info->CFM2); + dJointSetHinge2Param(joint->ode_joint, dParamStopERP2, info->ERP2); + dJointSetHinge2Param(joint->ode_joint, dParamVel2, info->Vel2); + break; + case JOINTTYPE_FIXED: + dJointSetFixed(joint->ode_joint); + break; + } + } +*/ +} + +static void QDECL World_Bullet_RagDestroyBody(world_t *world, rbebody_t *bodyptr) +{ +/* + if (bodyptr->ode_geom) + dGeomDestroy(bodyptr->ode_geom); + bodyptr->ode_geom = NULL; + if (bodyptr->ode_body) + dBodyDestroy(bodyptr->ode_body); + bodyptr->ode_body = NULL; +*/ +} + +static void QDECL World_Bullet_RagDestroyJoint(world_t *world, rbejoint_t *joint) +{ +/* + if (joint->ode_joint) + dJointDestroy(joint->ode_joint); + joint->ode_joint = NULL; +*/ +} + +//bullet gives us a handy way to get/set motion states. we can cheesily update entity fields this way. +class QCMotionState : public btMotionState +{ + wedict_t *edict; + qboolean dirty; + btTransform trans; + world_t *world; + + +public: + void ReloadMotionState(void) + { + vec3_t offset; + vec3_t axis[3]; + btVector3 org; + rbefuncs->AngleVectors(edict->v->angles, axis[0], axis[1], axis[2]); + VectorNegate(axis[1], axis[1]); + VectorAvg(edict->ode.ode_mins, edict->ode.ode_maxs, offset); + VectorMA(edict->v->origin, offset[0]*1, axis[0], org); + VectorMA(org, offset[1]*1, axis[1], org); + VectorMA(org, offset[2]*1, axis[2], org); + + trans.setBasis(btMatrix3x3(axis[0][0], axis[1][0], axis[2][0], + axis[0][1], axis[1][1], axis[2][1], + axis[0][2], axis[1][2], axis[2][2])); + 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 + { + worldTrans = trans; + } + + virtual void setWorldTransform(const btTransform &worldTrans) + { + vec3_t fwd, left, up, offset; + qboolean meshmodel; + trans = worldTrans; + + btVector3 pos = worldTrans.getOrigin(); + VectorCopy(worldTrans.getBasis().getColumn(0), fwd); + VectorCopy(worldTrans.getBasis().getColumn(1), left); + VectorCopy(worldTrans.getBasis().getColumn(2), up); + VectorAvg(edict->ode.ode_mins, edict->ode.ode_maxs, offset); + VectorMA(pos, offset[0]*-1, fwd, pos); + VectorMA(pos, offset[1]*-1, left, pos); + VectorMA(pos, offset[2]*-1, up, edict->v->origin); + + if (edict->v->modelindex) + { + model_t *model = world->Get_CModel(world, edict->v->modelindex); + if (model && (model->type == mod_alias || model->type == mod_halflife)) + meshmodel = qtrue; + else meshmodel = qfalse; + } + else meshmodel = qfalse; + + rbefuncs->VectorAngles(fwd, up, edict->v->angles, meshmodel); + + //so it doesn't get rebuilt + VectorCopy(edict->v->origin, edict->ode.ode_origin); + VectorCopy(edict->v->angles, edict->ode.ode_angles); + +// World_LinkEdict(world, edict, false); + +// if(mSceneNode == nullptr) +// return; // silently return before we set a node + +// btQuaternion rot = worldTrans.getRotation(); +// 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) +{ + bulletcontext_t *ctx = (bulletcontext_t*)world->rbe; + btRigidBody *body = NULL; + btScalar mass; + float test; + void *dataID; + model_t *model; + int axisindex; + int modelindex = 0; + int movetype = MOVETYPE_NONE; + int solid = SOLID_NOT; + int geomtype = GEOMTYPE_SOLID; + qboolean modified = qfalse; + vec3_t angles; + vec3_t avelocity; + vec3_t entmaxs; + vec3_t entmins; + vec3_t forward; + vec3_t geomcenter; + vec3_t geomsize; + vec3_t left; + vec3_t origin; + vec3_t spinvelocity; + vec3_t up; + vec3_t velocity; + vec_t f; + vec_t length; + vec_t massval = 1.0f; +// vec_t movelimit; + vec_t radius; + vec_t scale; + vec_t spinlimit; + qboolean gravity; + + geomtype = (int)ed->xv->geomtype; + solid = (int)ed->v->solid; + movetype = (int)ed->v->movetype; + scale = ed->xv->scale?ed->xv->scale:1; + modelindex = 0; + model = NULL; + + if (!geomtype) + { + switch((int)ed->v->solid) + { + case SOLID_NOT: geomtype = GEOMTYPE_NONE; break; + case SOLID_TRIGGER: geomtype = GEOMTYPE_NONE; break; + case SOLID_BSP: geomtype = GEOMTYPE_TRIMESH; break; + case SOLID_PHYSICS_TRIMESH: geomtype = GEOMTYPE_TRIMESH; break; + case SOLID_PHYSICS_BOX: geomtype = GEOMTYPE_BOX; break; + case SOLID_PHYSICS_SPHERE: geomtype = GEOMTYPE_SPHERE; break; + case SOLID_PHYSICS_CAPSULE: geomtype = GEOMTYPE_CAPSULE; break; + case SOLID_PHYSICS_CYLINDER:geomtype = GEOMTYPE_CYLINDER; break; + default: geomtype = GEOMTYPE_BOX; break; + } + } + + switch(geomtype) + { + case GEOMTYPE_TRIMESH: + modelindex = (int)ed->v->modelindex; + model = world->Get_CModel(world, modelindex); + if (!model || model->loadstate != MLS_LOADED) + { + model = NULL; + modelindex = 0; + } + if (model) + { + VectorScale(model->mins, scale, entmins); + VectorScale(model->maxs, scale, entmaxs); + if (ed->xv->mass) + massval = ed->xv->mass; + } + else + { + modelindex = 0; + massval = 1.0f; + } + massval = 0; //bullet does not support trisoup moving through the world. + break; + case GEOMTYPE_BOX: + case GEOMTYPE_SPHERE: + case GEOMTYPE_CAPSULE: + case GEOMTYPE_CAPSULE_X: + case GEOMTYPE_CAPSULE_Y: + case GEOMTYPE_CAPSULE_Z: + case GEOMTYPE_CYLINDER: + case GEOMTYPE_CYLINDER_X: + case GEOMTYPE_CYLINDER_Y: + case GEOMTYPE_CYLINDER_Z: + VectorCopy(ed->v->mins, entmins); + VectorCopy(ed->v->maxs, entmaxs); + if (ed->xv->mass) + massval = ed->xv->mass; + break; + default: +// case GEOMTYPE_NONE: + if (ed->ode.ode_physics) + World_Bullet_RemoveFromEntity(world, ed); + return; + } + + VectorSubtract(entmaxs, entmins, geomsize); + if (DotProduct(geomsize,geomsize) == 0) + { + // we don't allow point-size physics objects... + if (ed->ode.ode_physics) + World_Bullet_RemoveFromEntity(world, ed); + return; + } + + // check if we need to create or replace the geom + if (!ed->ode.ode_physics + || !VectorCompare(ed->ode.ode_mins, entmins) + || !VectorCompare(ed->ode.ode_maxs, entmaxs) + || ed->ode.ode_modelindex != modelindex) + { + btCollisionShape *geom; + + modified = qtrue; + World_Bullet_RemoveFromEntity(world, ed); + ed->ode.ode_physics = qtrue; + VectorCopy(entmins, ed->ode.ode_mins); + VectorCopy(entmaxs, ed->ode.ode_maxs); + ed->ode.ode_modelindex = modelindex; + VectorAvg(entmins, entmaxs, geomcenter); + ed->ode.ode_movelimit = min(geomsize[0], min(geomsize[1], geomsize[2])); + +/* memset(ed->ode.ode_offsetmatrix, 0, sizeof(ed->ode.ode_offsetmatrix)); + ed->ode.ode_offsetmatrix[0] = 1; + ed->ode.ode_offsetmatrix[5] = 1; + ed->ode.ode_offsetmatrix[10] = 1; + ed->ode.ode_offsetmatrix[3] = -geomcenter[0]; + ed->ode.ode_offsetmatrix[7] = -geomcenter[1]; + ed->ode.ode_offsetmatrix[11] = -geomcenter[2]; +*/ + ed->ode.ode_mass = massval; + + switch(geomtype) + { + case GEOMTYPE_TRIMESH: +// foo Matrix4x4_Identity(ed->ode.ode_offsetmatrix); + geom = NULL; + if (!model) + { + Con_Printf("entity %i (classname %s) has no model\n", NUM_FOR_EDICT(world->progs, (edict_t*)ed), PR_GetString(world->progs, ed->v->classname)); + if (ed->ode.ode_physics) + World_Bullet_RemoveFromEntity(world, ed); + return; + } + if (!rbefuncs->GenerateCollisionMesh(world, model, ed, geomcenter)) + { + if (ed->ode.ode_physics) + World_Bullet_RemoveFromEntity(world, ed); + return; + } + +// foo Matrix4x4_RM_CreateTranslate(ed->ode.ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2]); + + { + btTriangleIndexVertexArray *tiva = new btTriangleIndexVertexArray(); + btIndexedMesh mesh; + mesh.m_vertexType = PHY_FLOAT; + mesh.m_indexType = PHY_INTEGER; + mesh.m_numTriangles = ed->ode.ode_numtriangles; + mesh.m_numVertices = ed->ode.ode_numvertices; + mesh.m_triangleIndexBase = (const unsigned char*)ed->ode.ode_element3i; + mesh.m_triangleIndexStride = sizeof(*ed->ode.ode_element3i)*3; + mesh.m_vertexBase = (const unsigned char*)ed->ode.ode_vertex3f; + mesh.m_vertexStride = sizeof(*ed->ode.ode_vertex3f)*3; + tiva->addIndexedMesh(mesh); + geom = new btBvhTriangleMeshShape(tiva, true); + } + break; + + case GEOMTYPE_BOX: + geom = new btBoxShape(btVector3(geomsize[0], geomsize[1], geomsize[2]) * 0.5); + break; + + case GEOMTYPE_SPHERE: + geom = new btSphereShape(geomsize[0] * 0.5f); + break; + + case GEOMTYPE_CAPSULE: + case GEOMTYPE_CAPSULE_X: + case GEOMTYPE_CAPSULE_Y: + case GEOMTYPE_CAPSULE_Z: + if (geomtype == GEOMTYPE_CAPSULE) + { + // the qc gives us 3 axis radius, the longest axis is the capsule axis + axisindex = 0; + if (geomsize[axisindex] < geomsize[1]) + axisindex = 1; + if (geomsize[axisindex] < geomsize[2]) + axisindex = 2; + } + else + axisindex = geomtype-GEOMTYPE_CAPSULE_X; + if (axisindex == 0) + radius = min(geomsize[1], geomsize[2]) * 0.5f; + else if (axisindex == 1) + radius = min(geomsize[0], geomsize[2]) * 0.5f; + else + radius = min(geomsize[0], geomsize[1]) * 0.5f; + length = geomsize[axisindex] - radius*2; + if (length <= 0) + { + radius -= (1 - length)*0.5; + length = 1; + } + if (axisindex == 0) + geom = new btCapsuleShapeX(radius, length); + else if (axisindex == 1) + geom = new btCapsuleShape(radius, length); + else + geom = new btCapsuleShapeZ(radius, length); + break; + + case GEOMTYPE_CYLINDER: + case GEOMTYPE_CYLINDER_X: + case GEOMTYPE_CYLINDER_Y: + case GEOMTYPE_CYLINDER_Z: + if (geomtype == GEOMTYPE_CYLINDER) + { + // the qc gives us 3 axis radius, the longest axis is the capsule axis + axisindex = 0; + if (geomsize[axisindex] < geomsize[1]) + axisindex = 1; + if (geomsize[axisindex] < geomsize[2]) + axisindex = 2; + } + else + axisindex = geomtype-GEOMTYPE_CYLINDER_X; + if (axisindex == 0) + geom = new btCylinderShapeX(btVector3(geomsize[0], geomsize[1], geomsize[2])*0.5); + else if (axisindex == 1) + geom = new btCylinderShape(btVector3(geomsize[0], geomsize[1], geomsize[2])*0.5); + else + geom = new btCylinderShapeZ(btVector3(geomsize[0], geomsize[1], geomsize[2])*0.5); + break; + + default: +// Con_Printf("World_Bullet_BodyFromEntity: unrecognized solid value %i was accepted by filter\n", solid); + if (ed->ode.ode_physics) + World_Bullet_RemoveFromEntity(world, ed); + return; + } +// Matrix3x4_InvertTo4x4_Simple(ed->ode.ode_offsetmatrix, ed->ode.ode_offsetimatrix); +// ed->ode.ode_massbuf = BZ_Malloc(sizeof(dMass)); +// memcpy(ed->ode.ode_massbuf, &mass, sizeof(dMass)); + + ed->ode.ode_geom = (void *)geom; + } + + //non-moving objects need to be static objects (and thus need 0 mass) + if (movetype != MOVETYPE_PHYSICS && movetype != MOVETYPE_WALK) //enabling kinematic objects for everything else destroys framerates (!movetype || movetype == MOVETYPE_PUSH) + massval = 0; + + //if the mass changes, we'll need to create a new body (but not the shape, so invalidate the current one) + if (ed->ode.ode_mass != massval) + { + ed->ode.ode_mass = massval; + body = (btRigidBody*)ed->ode.ode_body; + if (body) + ctx->dworld->removeRigidBody(body); + ed->ode.ode_body = NULL; + } + +// if(ed->ode.ode_geom) +// dGeomSetData(ed->ode.ode_geom, (void*)ed); + if (movetype == MOVETYPE_PHYSICS && ed->ode.ode_mass) + { + if (ed->ode.ode_body == NULL) + { +// ed->ode.ode_body = (void *)(body = dBodyCreate(world->ode.ode_world)); +// dGeomSetBody(ed->ode.ode_geom, body); +// dBodySetData(body, (void*)ed); +// dBodySetMass(body, (dMass *) ed->ode.ode_massbuf); + + btVector3 fallInertia(0, 0, 0); + ((btCollisionShape*)ed->ode.ode_geom)->calculateLocalInertia(ed->ode.ode_mass, fallInertia); + btRigidBody::btRigidBodyConstructionInfo fallRigidBodyCI(ed->ode.ode_mass, new QCMotionState(ed,world), (btCollisionShape*)ed->ode.ode_geom, fallInertia); + body = new btRigidBody(fallRigidBodyCI); + body->setUserPointer(ed); +// btTransform trans; +// trans.setFromOpenGLMatrix(ed->ode.ode_offsetmatrix); +// body->setCenterOfMassTransform(trans); + ed->ode.ode_body = (void*)body; + + //continuous collision detection prefers using a sphere within the object. tbh I have no idea what values to specify. + body->setCcdMotionThreshold((geomsize[0]+geomsize[1]+geomsize[2])*(4/3)); + body->setCcdSweptSphereRadius((geomsize[0]+geomsize[1]+geomsize[2])*(0.5/3)); + + ctx->dworld->addRigidBody(body, ed->xv->dimension_solid, ed->xv->dimension_hit); + + modified = qtrue; + } + } + else + { + if (ed->ode.ode_body == NULL) + { + btRigidBody::btRigidBodyConstructionInfo rbci(ed->ode.ode_mass, new QCMotionState(ed,world), (btCollisionShape*)ed->ode.ode_geom, btVector3(0, 0, 0)); + body = new btRigidBody(rbci); + body->setUserPointer(ed); +// btTransform trans; +// trans.setFromOpenGLMatrix(ed->ode.ode_offsetmatrix); +// body->setCenterOfMassTransform(trans); + ed->ode.ode_body = (void*)body; + if (ed->ode.ode_mass) + body->setCollisionFlags(body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT); + else + body->setCollisionFlags(body->getCollisionFlags() | btCollisionObject::CF_STATIC_OBJECT); + ctx->dworld->addRigidBody(body, ed->xv->dimension_solid, ed->xv->dimension_hit); + + modified = qtrue; + } + } + + body = (btRigidBody*)ed->ode.ode_body; + + // get current data from entity + gravity = qtrue; + VectorCopy(ed->v->origin, origin); + VectorCopy(ed->v->velocity, velocity); + VectorCopy(ed->v->angles, angles); + VectorCopy(ed->v->avelocity, avelocity); + if (ed == world->edicts || (ed->xv->gravity && ed->xv->gravity <= 0.01)) + gravity = qfalse; + + // compatibility for legacy entities +// if (!DotProduct(forward,forward) || solid == SOLID_BSP) + { + vec3_t qangles, qavelocity; + VectorCopy(angles, qangles); + VectorCopy(avelocity, qavelocity); + + if (ed->v->modelindex) + { + model = world->Get_CModel(world, ed->v->modelindex); + if (!model || model->type == mod_alias) + { + qangles[PITCH] *= -1; + qavelocity[PITCH] *= -1; + } + } + + rbefuncs->AngleVectors(qangles, forward, left, up); + VectorNegate(left,left); + // convert single-axis rotations in avelocity to spinvelocity + // FIXME: untested math - check signs + VectorSet(spinvelocity, DEG2RAD(qavelocity[PITCH]), DEG2RAD(qavelocity[ROLL]), DEG2RAD(qavelocity[YAW])); + } + + // compatibility for legacy entities + switch (solid) + { + case SOLID_BBOX: + case SOLID_SLIDEBOX: + case SOLID_CORPSE: + VectorSet(forward, 1, 0, 0); + VectorSet(left, 0, 1, 0); + VectorSet(up, 0, 0, 1); + VectorSet(spinvelocity, 0, 0, 0); + break; + } + + + // we must prevent NANs... + test = DotProduct(origin,origin) + DotProduct(forward,forward) + DotProduct(left,left) + DotProduct(up,up) + DotProduct(velocity,velocity) + DotProduct(spinvelocity,spinvelocity); + if (IS_NAN(test)) + { + modified = qtrue; + //Con_Printf("Fixing NAN values on entity %i : .classname = \"%s\" .origin = '%f %f %f' .velocity = '%f %f %f' .axis_forward = '%f %f %f' .axis_left = '%f %f %f' .axis_up = %f %f %f' .spinvelocity = '%f %f %f'\n", PRVM_NUM_FOR_EDICT(ed), PRVM_GetString(PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.classname)->string), origin[0], origin[1], origin[2], velocity[0], velocity[1], velocity[2], forward[0], forward[1], forward[2], left[0], left[1], left[2], up[0], up[1], up[2], spinvelocity[0], spinvelocity[1], spinvelocity[2]); + Con_Printf("Fixing NAN values on entity %i : .classname = \"%s\" .origin = '%f %f %f' .velocity = '%f %f %f' .angles = '%f %f %f' .avelocity = '%f %f %f'\n", NUM_FOR_EDICT(world->progs, (edict_t*)ed), PR_GetString(world->progs, ed->v->classname), origin[0], origin[1], origin[2], velocity[0], velocity[1], velocity[2], angles[0], angles[1], angles[2], avelocity[0], avelocity[1], avelocity[2]); + test = DotProduct(origin,origin); + if (IS_NAN(test)) + VectorClear(origin); + test = DotProduct(forward,forward) * DotProduct(left,left) * DotProduct(up,up); + if (IS_NAN(test)) + { + VectorSet(angles, 0, 0, 0); + VectorSet(forward, 1, 0, 0); + VectorSet(left, 0, 1, 0); + VectorSet(up, 0, 0, 1); + } + test = DotProduct(velocity,velocity); + if (IS_NAN(test)) + VectorClear(velocity); + test = DotProduct(spinvelocity,spinvelocity); + if (IS_NAN(test)) + { + VectorClear(avelocity); + VectorClear(spinvelocity); + } + } + + // check if the qc edited any position data + if ( + 0//!VectorCompare(origin, ed->ode.ode_origin) + || !VectorCompare(velocity, ed->ode.ode_velocity) + //|| !VectorCompare(angles, ed->ode.ode_angles) + || !VectorCompare(avelocity, ed->ode.ode_avelocity) + || gravity != ed->ode.ode_gravity) + modified = qtrue; + + // store the qc values into the physics engine + body = (btRigidBody*)ed->ode.ode_body; + if (modified && body) + { +// dVector3 r[3]; + float entitymatrix[16]; + float bodymatrix[16]; + +#if 0 + Con_Printf("entity %i got changed by QC\n", (int) (ed - prog->edicts)); + if(!VectorCompare(origin, ed->ode.ode_origin)) + Con_Printf(" origin: %f %f %f -> %f %f %f\n", ed->ode.ode_origin[0], ed->ode.ode_origin[1], ed->ode.ode_origin[2], origin[0], origin[1], origin[2]); + if(!VectorCompare(velocity, ed->ode.ode_velocity)) + Con_Printf(" velocity: %f %f %f -> %f %f %f\n", ed->ode.ode_velocity[0], ed->ode.ode_velocity[1], ed->ode.ode_velocity[2], velocity[0], velocity[1], velocity[2]); + if(!VectorCompare(angles, ed->ode.ode_angles)) + Con_Printf(" angles: %f %f %f -> %f %f %f\n", ed->ode.ode_angles[0], ed->ode.ode_angles[1], ed->ode.ode_angles[2], angles[0], angles[1], angles[2]); + if(!VectorCompare(avelocity, ed->ode.ode_avelocity)) + Con_Printf(" avelocity: %f %f %f -> %f %f %f\n", ed->ode.ode_avelocity[0], ed->ode.ode_avelocity[1], ed->ode.ode_avelocity[2], avelocity[0], avelocity[1], avelocity[2]); + if(gravity != ed->ode.ode_gravity) + Con_Printf(" gravity: %i -> %i\n", ed->ide.ode_gravity, gravity); +#endif + + // values for BodyFromEntity to check if the qc modified anything later + VectorCopy(origin, ed->ode.ode_origin); + VectorCopy(velocity, ed->ode.ode_velocity); + VectorCopy(angles, ed->ode.ode_angles); + VectorCopy(avelocity, ed->ode.ode_avelocity); + ed->ode.ode_gravity = gravity; + +// foo Matrix4x4_RM_FromVectors(entitymatrix, forward, left, up, origin); +// foo Matrix4_Multiply(ed->ode.ode_offsetmatrix, entitymatrix, bodymatrix); +// foo Matrix3x4_RM_ToVectors(bodymatrix, forward, left, up, origin); + +// r[0][0] = forward[0]; +// r[1][0] = forward[1]; +// r[2][0] = forward[2]; +// r[0][1] = left[0]; +// r[1][1] = left[1]; +// r[2][1] = left[2]; +// r[0][2] = up[0]; +// r[1][2] = up[1]; +// r[2][2] = up[2]; + + QCMotionState *ms = (QCMotionState*)body->getMotionState(); + ms->ReloadMotionState(); + body->setMotionState(ms); + body->setLinearVelocity(btVector3(velocity[0], velocity[1], velocity[2])); + body->setAngularVelocity(btVector3(spinvelocity[0], spinvelocity[1], spinvelocity[2])); +// body->setGravity(btVector3(ed->xv->gravitydir[0], ed->xv->gravitydir[1], ed->xv->gravitydir[2]) * ed->xv->gravity); + + //something changed. make sure it still falls over appropriately + body->setActivationState(1); + } + +/* FIXME: check if we actually need this insanity with bullet (ode sucks). + if(body) + { + // limit movement speed to prevent missed collisions at high speed + btVector3 ovelocity = body->getLinearVelocity(); + btVector3 ospinvelocity = body->getAngularVelocity(); + movelimit = ed->ode.ode_movelimit * world->ode.ode_movelimit; + test = DotProduct(ovelocity,ovelocity); + if (test > movelimit*movelimit) + { + // scale down linear velocity to the movelimit + // scale down angular velocity the same amount for consistency + f = movelimit / sqrt(test); + VectorScale(ovelocity, f, velocity); + VectorScale(ospinvelocity, f, spinvelocity); + body->setLinearVelocity(btVector3(velocity[0], velocity[1], velocity[2])); + body->setAngularVelocity(btVector3(spinvelocity[0], spinvelocity[1], spinvelocity[2])); + } + + // make sure the angular velocity is not exploding + spinlimit = physics_bullet_spinlimit.value; + test = DotProduct(ospinvelocity,ospinvelocity); + if (test > spinlimit) + { + body->setAngularVelocity(btVector3(0, 0, 0)); + } + } +*/ +} + +/* +#define MAX_CONTACTS 16 +static void VARGS nearCallback (void *data, dGeomID o1, dGeomID o2) +{ + world_t *world = (world_t *)data; + dContact contact[MAX_CONTACTS]; // max contacts per collision pair + dBodyID b1; + dBodyID b2; + dJointID c; + int i; + int numcontacts; + + float bouncefactor1 = 0.0f; + float bouncestop1 = 60.0f / 800.0f; + float bouncefactor2 = 0.0f; + float bouncestop2 = 60.0f / 800.0f; + float erp; + dVector3 grav; + wedict_t *ed1, *ed2; + + if (dGeomIsSpace(o1) || dGeomIsSpace(o2)) + { + // colliding a space with something + dSpaceCollide2(o1, o2, data, &nearCallback); + // Note we do not want to test intersections within a space, + // only between spaces. + //if (dGeomIsSpace(o1)) dSpaceCollide(o1, data, &nearCallback); + //if (dGeomIsSpace(o2)) dSpaceCollide(o2, data, &nearCallback); + return; + } + + b1 = dGeomGetBody(o1); + b2 = dGeomGetBody(o2); + + // at least one object has to be using MOVETYPE_PHYSICS or we just don't care + if (!b1 && !b2) + return; + + // exit without doing anything if the two bodies are connected by a joint + if (b1 && b2 && dAreConnectedExcluding(b1, b2, dJointTypeContact)) + return; + + ed1 = (wedict_t *) dGeomGetData(o1); + ed2 = (wedict_t *) dGeomGetData(o2); + if (ed1 == ed2 && ed1) + { + //ragdolls don't make contact with the bbox of the doll entity + //the origional entity should probably not be solid anyway. + //these bodies should probably not collide against bboxes of other entities with ragdolls either, but meh. + if (ed1->ode.ode_body == b1 || ed2->ode.ode_body == b2) + return; + } + if(!ed1 || ed1->isfree) + ed1 = world->edicts; + if(!ed2 || ed2->isfree) + ed2 = world->edicts; + + // generate contact points between the two non-space geoms + numcontacts = dCollide(o1, o2, MAX_CONTACTS, &(contact[0].geom), sizeof(contact[0])); + if (numcontacts) + { + if(ed1 && ed1->v->touch) + { + world->Event_Touch(world, ed1, ed2); + } + if(ed2 && ed2->v->touch) + { + world->Event_Touch(world, ed2, ed1); + } + + // if either ent killed itself, don't collide + if ((ed1&&ed1->isfree) || (ed2&&ed2->isfree)) + return; + } + + if(ed1) + { + if (ed1->xv->bouncefactor) + bouncefactor1 = ed1->xv->bouncefactor; + + if (ed1->xv->bouncestop) + bouncestop1 = ed1->xv->bouncestop; + } + + if(ed2) + { + if (ed2->xv->bouncefactor) + bouncefactor2 = ed2->xv->bouncefactor; + + if (ed2->xv->bouncestop) + bouncestop2 = ed2->xv->bouncestop; + } + + if ((ed2->entnum&&ed1->v->owner == ed2->entnum) || (ed1->entnum&&ed2->v->owner == ed1->entnum)) + return; + + // merge bounce factors and bounce stop + if(bouncefactor2 > 0) + { + if(bouncefactor1 > 0) + { + // TODO possibly better logic to merge bounce factor data? + if(bouncestop2 < bouncestop1) + bouncestop1 = bouncestop2; + if(bouncefactor2 > bouncefactor1) + bouncefactor1 = bouncefactor2; + } + else + { + bouncestop1 = bouncestop2; + bouncefactor1 = bouncefactor2; + } + } + dWorldGetGravity(world->ode.ode_world, grav); + bouncestop1 *= fabs(grav[2]); + + erp = (DotProduct(ed1->v->velocity, ed1->v->velocity) > DotProduct(ed2->v->velocity, ed2->v->velocity)) ? ed1->xv->erp : ed2->xv->erp; + + // add these contact points to the simulation + for (i = 0;i < numcontacts;i++) + { + contact[i].surface.mode = (physics_bullet_contact_mu.value != -1 ? dContactApprox1 : 0) | + (physics_bullet_contact_erp.value != -1 ? dContactSoftERP : 0) | + (physics_bullet_contact_cfm.value != -1 ? dContactSoftCFM : 0) | + (bouncefactor1 > 0 ? dContactBounce : 0); + contact[i].surface.mu = physics_bullet_contact_mu.value; + if (ed1->xv->friction) + contact[i].surface.mu *= ed1->xv->friction; + if (ed2->xv->friction) + contact[i].surface.mu *= ed2->xv->friction; + contact[i].surface.mu2 = 0; + contact[i].surface.soft_erp = physics_bullet_contact_erp.value + erp; + contact[i].surface.soft_cfm = physics_bullet_contact_cfm.value; + contact[i].surface.bounce = bouncefactor1; + contact[i].surface.bounce_vel = bouncestop1; + c = dJointCreateContact(world->ode.ode_world, world->ode.ode_contactgroup, contact + i); + dJointAttach(c, b1, b2); + } +} +*/ + +static void QDECL World_Bullet_Frame(world_t *world, double frametime, double gravity) +{ + struct bulletcontext_s *ctx = (struct bulletcontext_s*)world->rbe; + if (world->rbe_hasphysicsents || ctx->hasextraobjs) + { + int i; + wedict_t *ed; + +// world->ode.ode_iterations = bound(1, physics_bullet_iterationsperframe.ival, 1000); +// world->ode.ode_step = frametime / world->ode.ode_iterations; +// world->ode.ode_movelimit = physics_bullet_movelimit.value / world->ode.ode_step; + + + // copy physics properties from entities to physics engine + for (i = 0;i < world->num_edicts;i++) + { + ed = (wedict_t*)EDICT_NUM(world->progs, i); + if (!ED_ISFREE(ed)) + World_Bullet_Frame_BodyFromEntity(world, ed); + } + // oh, and it must be called after all bodies were created + for (i = 0;i < world->num_edicts;i++) + { + ed = (wedict_t*)EDICT_NUM(world->progs, i); + if (!ED_ISFREE(ed)) + World_Bullet_Frame_JointFromEntity(world, ed); + } + while(ctx->cmdqueuehead) + { + rbecommandqueue_t *cmd = ctx->cmdqueuehead; + ctx->cmdqueuehead = cmd->next; + if (!cmd->next) + ctx->cmdqueuetail = NULL; + World_Bullet_RunCmd(world, cmd); + Z_Free(cmd); + } + + ctx->dworld->setGravity(btVector3(0, 0, -gravity)); + + ctx->dworld->stepSimulation(frametime, max(0, pCvar_GetFloat("physics_bullet_maxiterationsperframe")), 1/bound(1, pCvar_GetFloat("physics_bullet_framerate"), 500)); + + // set the tolerance for closeness of objects +// dWorldSetContactSurfaceLayer(world->ode.ode_world, max(0, physics_bullet_contactsurfacelayer.value)); + + // run collisions for the current world state, creating JointGroup +// dSpaceCollide(world->ode.ode_space, (void *)world, nearCallback); + + // run physics (move objects, calculate new velocities) +// if (physics_bullet_worldquickstep.ival) +// { +// dWorldSetQuickStepNumIterations(world->ode.ode_world, bound(1, physics_bullet_worldquickstep_iterations.ival, 200)); +// dWorldQuickStep(world->ode.ode_world, world->ode.ode_step); +// } +// else +// dWorldStep(world->ode.ode_world, world->ode.ode_step); + + // clear the JointGroup now that we're done with it +// dJointGroupEmpty(world->ode.ode_contactgroup); + + if (world->rbe_hasphysicsents) + { + // copy physics properties from physics engine to entities + for (i = 1;i < world->num_edicts;i++) + { + ed = (wedict_t*)EDICT_NUM(world->progs, i); + if (!ED_ISFREE(ed)) + World_Bullet_Frame_BodyToEntity(world, ed); + } + } + } +} + +static void World_Bullet_RunCmd(world_t *world, rbecommandqueue_t *cmd) +{ + btRigidBody *body = (btRigidBody*)(cmd->edict->ode.ode_body); + switch(cmd->command) + { + case RBECMD_ENABLE: + if (body) + body->setActivationState(1); + break; + case RBECMD_DISABLE: + if (body) + body->setActivationState(0); + break; + case RBECMD_FORCE: + if (body) + { + body->setActivationState(1); + body->applyForce(btVector3(cmd->v1[0], cmd->v1[1], cmd->v1[2]), btVector3(cmd->v2[0], cmd->v2[1], cmd->v2[2])); + } + break; + case RBECMD_TORQUE: + if (cmd->edict->ode.ode_body) + { + body->setActivationState(1); + body->applyTorque(btVector3(cmd->v1[0], cmd->v1[1], cmd->v1[2])); + } + break; + } +} + +static void QDECL World_Bullet_PushCommand(world_t *world, rbecommandqueue_t *val) +{ + struct bulletcontext_s *ctx = (struct bulletcontext_s*)world->rbe; + rbecommandqueue_t *cmd = (rbecommandqueue_t*)BZ_Malloc(sizeof(*cmd)); + world->rbe_hasphysicsents = qtrue; //just in case. + memcpy(cmd, val, sizeof(*cmd)); + cmd->next = NULL; + //add on the end of the queue, so that order is preserved. + if (ctx->cmdqueuehead) + { + rbecommandqueue_t *ot = ctx->cmdqueuetail; + ot->next = ctx->cmdqueuetail = cmd; + } + else + ctx->cmdqueuetail = ctx->cmdqueuehead = cmd; +} + +static void QDECL World_Bullet_TraceEntity(world_t *world, vec3_t start, vec3_t end, wedict_t *ed) +{ + struct bulletcontext_s *ctx = (struct bulletcontext_s*)world->rbe; + btCollisionShape *shape = (btCollisionShape*)ed->ode.ode_geom; + + class myConvexResultCallback : public btCollisionWorld::ConvexResultCallback + { + public: + virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace) + { + return 0; + } + } result; + + btTransform from(btMatrix3x3(1, 0, 0, 0, 1, 0, 0, 0, 1), btVector3(start[0], start[1], start[2])); + btTransform to(btMatrix3x3(1, 0, 0, 0, 1, 0, 0, 0, 1), btVector3(end[0], end[1], end[2])); + ctx->dworld->convexSweepTest((btConvexShape*)shape, from, to, result, 1); +} + +static void QDECL World_Bullet_Start(world_t *world) +{ + struct bulletcontext_s *ctx; + if (world->rbe) + return; //no thanks, we already have one. somehow. + + if (!pCvar_GetFloat("physics_bullet_enable")) + return; + + ctx = (struct bulletcontext_s*)BZ_Malloc(sizeof(*ctx)); + memset(ctx, 0, sizeof(*ctx)); + ctx->gworld = world; + ctx->funcs.End = World_Bullet_End; + ctx->funcs.RemoveJointFromEntity = World_Bullet_RemoveJointFromEntity; + ctx->funcs.RemoveFromEntity = World_Bullet_RemoveFromEntity; + ctx->funcs.RagMatrixToBody = World_Bullet_RagMatrixToBody; + ctx->funcs.RagCreateBody = World_Bullet_RagCreateBody; + ctx->funcs.RagMatrixFromJoint = World_Bullet_RagMatrixFromJoint; + ctx->funcs.RagMatrixFromBody = World_Bullet_RagMatrixFromBody; + ctx->funcs.RagEnableJoint = World_Bullet_RagEnableJoint; + ctx->funcs.RagCreateJoint = World_Bullet_RagCreateJoint; + ctx->funcs.RagDestroyBody = World_Bullet_RagDestroyBody; + ctx->funcs.RagDestroyJoint = World_Bullet_RagDestroyJoint; + ctx->funcs.RunFrame = World_Bullet_Frame; + ctx->funcs.PushCommand = World_Bullet_PushCommand; + world->rbe = &ctx->funcs; + + + ctx->broadphase = new btDbvtBroadphase(); + ctx->collisionconfig = new btDefaultCollisionConfiguration(); + ctx->collisiondispatcher = new btCollisionDispatcher(ctx->collisionconfig); + ctx->solver = new btSequentialImpulseConstraintSolver; + ctx->dworld = new btDiscreteDynamicsWorld(ctx->collisiondispatcher, ctx->broadphase, ctx->solver, ctx->collisionconfig); + + ctx->ownerfilter = new QCFilterCallback(); + ctx->dworld->getPairCache()->setOverlapFilterCallback(ctx->ownerfilter); + + + + ctx->dworld->setGravity(btVector3(0, -10, 0)); + +/* + if(physics_bullet_world_erp.value >= 0) + dWorldSetERP(world->ode.ode_world, physics_bullet_world_erp.value); + if(physics_bullet_world_cfm.value >= 0) + dWorldSetCFM(world->ode.ode_world, physics_bullet_world_cfm.value); + if (physics_bullet_world_damping.ival) + { + dWorldSetLinearDamping(world->ode.ode_world, (physics_bullet_world_damping_linear.value >= 0) ? (physics_bullet_world_damping_linear.value * physics_bullet_world_damping.value) : 0); + dWorldSetLinearDampingThreshold(world->ode.ode_world, (physics_bullet_world_damping_linear_threshold.value >= 0) ? (physics_bullet_world_damping_linear_threshold.value * physics_bullet_world_damping.value) : 0); + dWorldSetAngularDamping(world->ode.ode_world, (physics_bullet_world_damping_angular.value >= 0) ? (physics_bullet_world_damping_angular.value * physics_bullet_world_damping.value) : 0); + dWorldSetAngularDampingThreshold(world->ode.ode_world, (physics_bullet_world_damping_angular_threshold.value >= 0) ? (physics_bullet_world_damping_angular_threshold.value * physics_bullet_world_damping.value) : 0); + } + else + { + dWorldSetLinearDamping(world->ode.ode_world, 0); + dWorldSetLinearDampingThreshold(world->ode.ode_world, 0); + dWorldSetAngularDamping(world->ode.ode_world, 0); + dWorldSetAngularDampingThreshold(world->ode.ode_world, 0); + } + if (physics_bullet_autodisable.ival) + { + dWorldSetAutoDisableSteps(world->ode.ode_world, bound(1, physics_bullet_autodisable_steps.ival, 100)); + dWorldSetAutoDisableTime(world->ode.ode_world, physics_bullet_autodisable_time.value); + dWorldSetAutoDisableAverageSamplesCount(world->ode.ode_world, bound(1, physics_bullet_autodisable_threshold_samples.ival, 100)); + dWorldSetAutoDisableLinearThreshold(world->ode.ode_world, physics_bullet_autodisable_threshold_linear.value); + dWorldSetAutoDisableAngularThreshold(world->ode.ode_world, physics_bullet_autodisable_threshold_angular.value); + dWorldSetAutoDisableFlag (world->ode.ode_world, true); + } + else + dWorldSetAutoDisableFlag (world->ode.ode_world, false); + */ +} + +static qintptr_t QDECL World_Bullet_Shutdown(qintptr_t *args) +{ + if (rbefuncs) + rbefuncs->UnregisterPhysicsEngine("Bullet"); + return 0; +} + +static bool World_Bullet_DoInit(void) +{ + if (!rbefuncs || !rbefuncs->RegisterPhysicsEngine) + { + rbefuncs = NULL; + Con_Printf("Bullet plugin failed: Engine doesn't support physics engine plugins.\n"); + } + else if (!rbefuncs->RegisterPhysicsEngine("Bullet", World_Bullet_Start)) + Con_Printf("Bullet plugin failed: Engine already has a physics plugin active.\n"); + else + { + World_Bullet_Init(); + return true; + } + return false; +} + +#define ARGNAMES ,version +static BUILTINR(rbeplugfuncs_t*, RBE_GetPluginFuncs, (int version)); +#undef ARGNAMES + +extern "C" qintptr_t Plug_Init(qintptr_t *args) +{ + CHECKBUILTIN(RBE_GetPluginFuncs); + + if (BUILTINISVALID(RBE_GetPluginFuncs)) + { + rbefuncs = pRBE_GetPluginFuncs(sizeof(rbeplugfuncs_t)); + if (rbefuncs && rbefuncs->version < RBEPLUGFUNCS_VERSION) + rbefuncs = NULL; + } + + Plug_Export("Shutdown", World_Bullet_Shutdown); + return World_Bullet_DoInit(); +} + +